文件上传及几个细节问题

来源:互联网 发布:欧陆风云4 mac dlc 编辑:程序博客网 时间:2024/06/05 13:45

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

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-fileuploadcommons-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.txtfileName = 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 blocke.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;}

0 0