您的位置:首页 > 其它

文件上传及几个细节问题

2014-02-17 23:23 281 查看
搭建网站时,有一个常用的功能就是文件的上传与下载。在添加一个文件上传功能时,主要有两个步骤:

1、创建一个web页面,填写需要上传文件的相关输入项

1.1通过<input type="file" />标签可以添加一个上传文件,需要注意的是,input标签一定要设置name属性,否则浏览器不会发送上传的文件数据。

1.2另外还需要把form标签的enctype属性设置成multipart/form-data,设置该值后,浏览器将会把文件数据附带在http请求消息体中,并使用MIME协议对上传文件进行描述,以方便接受方对上传文件进行解析和处理

2、创建一个servlet,负责解析上传过来的文件,读取并将其保存至硬盘中

2.1 request提供了一个getInputStream方法,可以获取到客户端提交过来的数据,但由于客户可能同时上传多个文件,在servelt上直接编程将会很麻烦。

2.2 apache组织提供一个用来处理表单文件上传的组件,该组件提供了性能优异的API,方便开发者实现表单文件上传功能。此组件名为:Commons-fileupload

3、使用Commons-fileupload组件,需要导入相关开发jar包,此jar包名为:

Commons-fileupload和commons-io, commons-io不属于文件上传组件的开发jar包,但Commons-fileupload从1.1版本后,需要其支持。

使用此开源组件,开发流程图如下:



4、Servlet核心开发步骤

4.1 创建一个DiskFileItemFactory工厂

DiskFileItemFactory factory = new DiskFileItemFactory();

4.2 创建一个ServletFileUpload对象,此对象将封装对上传文件数据的处理操作

ServletFileUpload upload = new ServletFileUpload(factory);

4.3 通过ServletFileUpload的isMultipartContent方法判断是否是表单文件上传,否则按照正常方式处理

if (!upload.isMultipartContent(request)) {
// 安传统方式读取数据,即表单数据上传的数据 enctype="multipart/form-data"
return;
}
4.4 通过ServletFileUpload的parseRequest方法获取request提交的每项表单数据,每项表单数据将被封装在一个FileItem对象中
List<FileItem> list;
list = (List<FileItem>) upload.parseRequest(request);
4.5 处理每项表单数据,根据FileItem的isFormField方法来区分提交的数据是否是普通表单数据还是文件上传数据,若是文件上传数据,调用FileItem的getInputStream方法可以获取到上传文件的数据。
整体代码示例如下:

public class FileUploadServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

// request是安装MIME协议传输,不能再以request.getParameter方式读取
String savePath = this.getServletContext().getRealPath(
"/WEB-INF/upload");

//1、解决表单名和上传文件名中文乱码问题
// request.setCharacterEncoding("UTF-8");
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);

//方法二:解决上传文件名中文乱码问题
upload.setHeaderEncoding("UTF-8");

if (!upload.isMultipartContent(request)) {
// 安传统方式读取数据,即表单数据上传的数据 enctype="multipart/form-data"
return;
}

//上传的数据时按照enctype="multipart/form-data"方式传上来的,其数据时按照MIME协议上传的
List<FileItem> list;
list = (List<FileItem>) upload.parseRequest(request);

for (FileItem item : list) {
if (item.isFormField()) {
String name = item.getFieldName();
//方法一:数据表单内容的中文乱码问题,按照multipart/form-data上传的数据,都按照iso8859-1进行编码
// 要解决此中文乱码问题,需要先对获取到的数据进行iso8859-1进行解码,在按照UTF-8进行编码
// String value = item.getString();
// value = new String(value.getBytes("iso8859-1"), "UTF-8");

//方法二:解决数据表单内容的中文乱码问题,采用FiltItem类中的getString(Charset)方法,此方法重载了getString
//获取到的数据将按照指定的编码方式进行编码
String value = item.getString("utf-8");

System.out.println(name + ":" + value);
} else {
String fileName = item.getName();
// 不同浏览器通过request上传的文件名不一致,有是完整路径,有点是文件名: c:\a\b\1.txt or
// 1.txt
fileName = fileName
.substring((fileName.lastIndexOf("\\") + 1));
InputStream in = item.getInputStream();

//FileOutputStream out = new FileOutputStream(savePath + "\\"
// + fileName);
//解决上传文件重名问题
String saveName = makeFileName(fileName);
// FileOutputStream out = new FileOutputStream(savePath + "\\"
// +saveName);

//文件打散算法
String realSavePath = makeSavePath(savePath, fileName);
FileOutputStream out = new FileOutputStream(realSavePath + "\\"
+saveName);

byte[] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf)) > 0) {
out.write(buf);
}

out.close();
in.close();
}
}
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

doGet(request, response);
}

//解决上传文件重名导致文件覆盖问题
private String makeFileName(String fileName){
return UUID.randomUUID()+"_"+fileName;
}

//文件打散问题,解决一个文件下文件过多的问题
private String makeSavePath(String savePath, String fileName){
if(fileName==null || savePath==null){
return null;
}

int hCode = fileName.hashCode();
int dir1 = hCode&0x0f;
int dir2 = (hCode&0xf0)>>4;

String dir = savePath+"\\"+dir1+"\\"+dir2;

//文件要是不存在,创建此文件
File f = new File(dir);
if(!f.exists()){
f.mkdirs();
}

return dir;
}

}
5 上面的示例代码还解决了几个文件上传中的细节问题

5.1 表单名和上传文件名中文乱码问题

解决此问题有两种方法

一:通过request.setCharacterEncoding("UTF-8")来设置中文编码

二:通过ServletFileUpload的setHeaderEncoding方法来设置中文编码

ServletFileUpload upload = new ServletFileUpload(factory);

//方法二:解决上传文件名中文乱码问题
upload.setHeaderEncoding("UTF-8");
5.2 提交的表单内容中文乱码问题

由于设置了form标签的enctype属性值为multipart/form-data,其将通过MIME协议向服务器提交数据,所有表单数据都是通过iso8859-1来编码的。解决此中文乱码问题有两种方法

一:将表单数据通过iso8859-1进行解码,得到源字节码,在通过UTF-8编码

if (item.isFormField()) {
String name = item.getFieldName();
//方法一:数据表单内容的中文乱码问题,按照multipart/form-data上传的数据,都按照iso8859-1进行编码
// 要解决此中文乱码问题,需要先对获取到的数据进行iso8859-1进行解码,在按照UTF-8进行编码
// String value = item.getString();
// value = new String(value.getBytes("iso8859-1"), "UTF-8");

//方法二:解决数据表单内容的中文乱码问题,采用FiltItem类中的getString(Charset)方法,此方法重载了getString
//获取到的数据将按照指定的编码方式进行编码
String value = item.getString("utf-8");

System.out.println(name + ":" + value);
二:通过调用FileItem的getString(Charset charset)来获取表单内容。

5.3 上传文件重名问题

使用UUID方法重写文件名

//解决上传文件重名导致文件覆盖问题
private String makeFileName(String fileName){
return UUID.randomUUID()+"_"+fileName;
}
5.4 上传文件打散问题

使用文件名在内存中的地址(hashcode)来创建存储路径,总共两级目录,第一级目录名为filename 哈希值最低0-4位值,第二级目录名为哈希值低5-8位。

private String makeSavePath(String savePath, String fileName){
if(fileName==null || savePath==null){
return null;
}

int hCode = fileName.hashCode();
int dir1 = hCode&0x0f;
int dir2 = (hCode&0xf0)>>4;

String dir = savePath+"\\"+dir1+"\\"+dir2;

//文件要是不存在,创建此文件
File f = new File(dir);
if(!f.exists()){
f.mkdirs();
}

return dir;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: