昨天做了一个Servlet实现文件上传的功能,之前没仔细研究过commons-fileupload-1.2.1.jar,就随意网上搜了下例子,草率写完了,测试成功,感觉不错没出什么问题,回来无意之间又看到一篇文章说,用commons-fileupload-1.2.1.jar实现上传文件一定要加上commons-io-1.3.2.jar,我就开始纳闷了,我明明没有加这个io包测试成功,他居然说必须加,感觉肯定是有问题的,经过今天一上午研究最后终于找出原因为什么有人说加有人说不加啦,预知详情,请不要走开,
1,先把我的servlet简单的写出来,还有个jsp,没什么内容就几个<input type="file" >,就不列出来了,不要忘了form里加上enctype="multipart/form-data",没这个貌似不可以的。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("UTF-8"); FileBiz biz = new FileBiz(); String uploadPath = getServletContext().getRealPath("/");//获取文件路径 biz.upload(request,uploadPath); response.getWriter().println("上传成功"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); }
2,列下我第一次没有加commons-io-1.3.2.jar情况下测试成功的代码。
public class FileBiz { public void upload(HttpServletRequest request,String uploadPath) { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss"); File tmpDir = new File("d:\\temp"); //初始化上传文件的临时存放目录,必须是绝对路径 try { if (ServletFileUpload.isMultipartContent(request)) { DiskFileItemFactory factory = new DiskFileItemFactory(); //指定在内存中缓存数据大小,单位为byte,这里设为1Mb factory.setSizeThreshold(1 * 1024 * 1024); //设置一旦文件大小超过getSizeThreshold()的值时数据存放在硬盘的目录 factory.setRepository(tmpDir); ServletFileUpload sfu = new ServletFileUpload(factory); // 指定单个上传文件的最大尺寸,单位:字节,这里设为5Mb sfu.setFileSizeMax(5 * 1024 * 1024); //指定一次上传多个文件的总尺寸,单位:字节,这里设为10Mb sfu.setSizeMax(10 * 1024 * 1024); sfu.setHeaderEncoding("UTF-8"); //设置编码,因为我的jsp页面的编码是utf-8的 FileItemIterator fii = sfu.getItemIterator(request);// 解析request请求 uploadPath = uploadPath + "upload\\"; // 选定上传的目录此处为当前目录 if (!new File(uploadPath).isDirectory()){ new File(uploadPath).mkdirs(); //选定上传的目录此处为当前目录,没有则创建 } int index = 0; while (fii.hasNext()) { FileItemStream fis = fii.next();// 从集合中获得一个文件流 if (!fis.isFormField() && fis.getName().length() > 0) {// 过滤掉表单中非文件域 String fileName = fis.getName().substring( fis.getName().lastIndexOf("."));// 获得上传文件的文件名 fileName = sdf.format(new Date())+"-"+index+fileName; BufferedInputStream in = new BufferedInputStream(fis.openStream()); BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(new File(uploadPath + "\\" + fileName))); Streams.copy(in, out, true); // 开始把文件写到你指定的上传文件夹 index++; } } } } catch (Exception e) { e.printStackTrace(); } } }
注意这里的写入文件时调用是commons.fileupload.util.Streams工具类的静态方法。
以上就可以实现上传了,这个是支持多文件上传的。
3,我想既然有人说需要加commons-io包的,要么是环境跟我的不一样,要么是实现的方法跟我的不一样,我的环境是
windows +tomcat6.0+jdk1.5,环境不容易改变,只有改变方法啦。
改变后的load方法。
public void uploads(HttpServletRequest request,String uploadPath){ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss"); File tmpDir = new File("d:\\temp"); try { if (ServletFileUpload.isMultipartContent(request)) { DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setSizeThreshold(1 * 1024 * 1024); factory.setRepository(tmpDir); ServletFileUpload sfu = new ServletFileUpload(factory); sfu.setFileSizeMax(5 * 1024 * 1024); sfu.setSizeMax(10 * 1024 * 1024); sfu.setHeaderEncoding("UTF-8"); List<FileItem> fileItems = sfu.parseRequest(request); uploadPath = uploadPath + "upload\\"; if (!new File(uploadPath).isDirectory()){ new File(uploadPath).mkdirs(); } int leng = fileItems.size(); for(int n=0;n<leng;n++) { FileItem item = fileItems.get(n); // 从集合中获得一个文件流 // 如果是普通表单字段 if(item.isFormField()) { String name = item.getFieldName(); // 获得该字段名称 String value = item.getString("utf-8"); //获得该字段值 System.out.println(name+value); }else if(item.getName().length()>0) { // 如果为文件域 String iname = item.getName().substring( item.getName().lastIndexOf(".")); String fname=sdf.format(new Date())+"-"+n+iname; try { item.write(new File(uploadPath, fname)); // 写入文件 } catch (Exception e) { e.printStackTrace(); } } } } }catch (Exception e) { e.printStackTrace(); } }
一测试,不行啦,报错了,马上加上commons-io-1.3.2.jar,再测试,OK。
原来是实现方法不一样,说不需要加包的都是基于第一种方法实现的,说需要加的都是第二种。
必须知道为什么会这样,下载commons-fileupload-1.2.1.jar源代码看看,
找到第一种方法源代码如下
public static long copy(InputStream pIn, OutputStream pOut, boolean pClose, byte[] pBuffer) throws IOException { OutputStream out = pOut; InputStream in = pIn; try { long total = 0; for (;;) { int res = in.read(pBuffer); if (res == -1) { break; } if (res > 0) { total += res; if (out != null) { out.write(pBuffer, 0, res); } } } if (out != null) { if (pClose) { out.close(); } else { out.flush(); } out = null; } in.close(); in = null; return total; } finally { if (in != null) { try { in.close(); } catch (Throwable t) { /* Ignore me */ } } if (pClose && out != null) { try { out.close(); } catch (Throwable t) { /* Ignore me */ } } } }
第二种源代码如下,FileItem是个interface,要找DiskFileItem类,
import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.DeferredFileOutputStream; public class DiskFileItem implements FileItem, FileItemHeadersSupport {
FileItem.write方法就是DiskFileItem.write方法,其中写文件时用的就是IOUitls类写的,
BufferedInputStream in = null; BufferedOutputStream out = null; try { in = new BufferedInputStream( new FileInputStream(outputFile)); out = new BufferedOutputStream( new FileOutputStream(file)); IOUtils.copy(in, out); } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } //方法有点长,把主要挖出来
最后还测试了下性能问题,简单测试显示第二种方法比快很多,毕竟commons-io-1.3.2.jar是专业处理io的嘛,哈哈
这应该就是加不加commons-io-1.3.2.jar的原因了吧。。