Commons FileUpload
Apache提供的一个组件,可以很方便的让我们处理客户端上传的文件,
下载地址 http://commons.apache.org/proper/commons-fileupload/
下载commons-fileupload.jar,还有其依赖包 commons-io.jar一同下载好,导入工程
有点奇葩的是,在tomcat下已经把這个组建集成了,但是不能正常使用,其源码基本上是一样的。
正确的包名应该是這个:org.apache.commons.fileupload.
而不是org.apache.tomcat.util.http.fileupload.下面的
最简单的使用
1 protected void doPost(HttpServletRequest request, 2 HttpServletResponse response) throws ServletException, IOException { 3 4 ServletContext servletContext = this.getServletContext(); 5 6 // 指定保存文件的位置,放在WEB-INF目录下是为了保证系统的安全性 7 String savePath = servletContext.getRealPath("/WEB-INF/upload"); 8 System.out.println("savePath=" + savePath); 9 10 if (ServletFileUpload.isMultipartContent(request)) { 11 12 try { 13 // 创建FileItem的工厂 14 DiskFileItemFactory factory = new DiskFileItemFactory(); 24 // 创建一个FileItem的解析器,根据前面设置好的工厂得到 25 ServletFileUpload upload = new ServletFileUpload(factory); 26 27 // 解析出FIleItem项,parserRequest接收的类型是 28 // javax.servlet.http.HttpServletRequest 29 List<FileItem> items = upload.parseRequest(request); 30 31 // 遍历FileItem,检查是否为普通字符还是文件流 32 for (FileItem item : items) { 33 34 // 普通表单文字 35 if (item.isFormField()) { 36 // 表单上的name 37 String name = item.getFieldName(); 38 // 表单上的value 39 String value = item.getString(); 40 41 System.out.println("name=" + name 42 + " ---------- value=" + value); 43 44 // 数据库操作。。 45 } else { 46 // 文件一般从网络上传后存放到数据库,而原来的文件将采用不重复的命名方案存放 47 String fileName = item.getName();// 文件名,可能是带全路径的或者就是文件名 48 System.out.println("上传文件全路径:" + fileName); 49 fileName = fileName.substring(fileName 50 .lastIndexOf("\") + 1);// IE 51 // 、Opera 52 System.out.println("上传文件名:" + fileName); 53 54 // 没有上传文件 55 if (fileName.equals("")) { 56 break; 57 } 58 InputStream in = item.getInputStream(); 59 FileOutputStream fos = new FileOutputStream(savePath 60 + "\" + fileName); 61 62 byte[] b = new byte[1024]; 63 int len = 0; 64 while ((len = in.read(b)) != -1) { 65 fos.write(b, 0, len); 66 } 67 68 in.close(); 69 fos.close(); 70 71 } 72 } 73 74 } catch (FileUploadException e) { 75 // TODO Auto-generated catch block 76 e.printStackTrace(); 77 } 78 79 } else { 80 response.getWriter().write("no multipart/form-data submit"); 81 } 82 } 83
设置一下高级的用法:
修改默认设置,设置限制上传大小,返回当前上传进度。。
protected void doPost(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); ServletContext servletContext = this.getServletContext(); // 指定保存文件的位置,放在WEB-INF目录下是为了保证系统的安全性 String savePath = servletContext.getRealPath("/WEB-INF/upload"); System.out.println("savePath=" + savePath); if (ServletFileUpload.isMultipartContent(request)) { try { // 创建FileItem的工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); // 设置FileItem工厂的一些属性 File repository = (File) servletContext .getAttribute("javax.servlet.context.tempdir"); // 得到用户临时文件夹 System.out.println("temp position:" + repository.getPath()); factory.setRepository(repository); // 设置临时的存储位置 factory.setSizeThreshold(1024 * 100);// 设置缓存大小为100kb,默认为10K,以字节为单位 // 创建一个FileItem的解析器,根据前面设置好的工厂得到 ServletFileUpload upload = new ServletFileUpload(factory); // 设置允许上传的最大值,超出则拒绝上传 upload.setSizeMax(1024 * 1000); // 设置上传时的进度条监听器 ProgressListener pListener = new ProgressListener() { @Override public void update(long pBytesRead, long pContentLength, int pItems) { try { response.getWriter().write( "上传第" + pItems + "文件 上传进度:" + (pBytesRead * 1.0 / pContentLength * 100) + "%<br/>"); } catch (IOException e) { e.printStackTrace(); } } }; upload.setProgressListener(pListener); // 解析出FIleItem项,parserRequest接收的类型是 // javax.servlet.http.HttpServletRequest List<FileItem> items = upload.parseRequest(request); // 遍历FileItem,检查是否为普通字符还是文件流 for (FileItem item : items) { // 普通表单文字 if (item.isFormField()) { // 表单上的name String name = item.getFieldName(); // 表单上的value String value = item.getString(); System.out.println("name=" + name + " ---------- value=" + value); // 数据库操作。。 } else { // 文件一般从网络上传后存放到数据库,而原来的文件将采用不重复的命名方案存放,System.currentTime String fileName = item.getName();// 文件名,可能是带全路径的或者就是文件名 System.out.println("上传文件全路径:" + fileName); fileName = fileName.substring(fileName .lastIndexOf("\") + 1);// IE // 、Opera System.out.println("上传文件名:" + fileName); // 没有上传文件 if (fileName.equals("")) { break; } InputStream in = item.getInputStream(); FileOutputStream fos = new FileOutputStream(savePath + "\" + getFileName(fileName)); byte[] b = new byte[1024]; int len = 0; while ((len = in.read(b)) != -1) { fos.write(b, 0, len); } in.close(); fos.close(); } } } catch (SizeLimitExceededException e1) { response.getWriter().write("超出大小啦。。"); } catch (FileUploadException e) { response.getWriter().write("上传失败了。。"); e.printStackTrace(); } } else { response.getWriter().write("no multipart/form-data submit"); } } String getFileName(String fileName) { Date date = new Date(System.currentTimeMillis()); String result = DateFormat.getDateInstance(DateFormat.SHORT).format( date) + "_" + fileName; return result; }
這个组建的目录结构如图
ServletFileUpload.isMultipartContent(request) 判断是否带有文件数据的表单
其内部其实还是调用了request.getContentType()的方法的
ServletUpload
1 public static final boolean isMultipartContent( 2 HttpServletRequest request) { 3 if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) { 4 return false; 5 } 6 return FileUploadBase.isMultipartContent(new ServletRequestContext(request)); 7 }
FileUploadBase
1 public static final boolean isMultipartContent(RequestContext ctx) { 2 String contentType = ctx.getContentType(); 3 if (contentType == null) { 4 return false; 5 } 6 if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) { 7 return true; 8 } 9 return false; 10 }
//获取迭代器,效率最高的
FileItemIterator fIterator = upload.getItemIterator(request);
//获取List集合,调用迭代器转换而来的
List<FileItem> items = upload.parseRequest(request);
//获取Map集合,调用list集合转换的
parseParameterMap(request)
迭代器实现过程:
1 FileItemIteratorImpl(RequestContext ctx) 2 throws FileUploadException, IOException { 3 if (ctx == null) { 4 throw new NullPointerException("ctx parameter"); 5 } 6 7 String contentType = ctx.getContentType(); 8 if ((null == contentType) 9 || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) { 10 throw new InvalidContentTypeException( 11 format("the request doesn't contain a %s or %s stream, content type header is %s", 12 MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType)); 13 } 14 15 InputStream input = ctx.getInputStream(); 16 17 @SuppressWarnings("deprecation") // still has to be backward compatible 18 final int contentLengthInt = ctx.getContentLength(); 19 20 final long requestSize = UploadContext.class.isAssignableFrom(ctx.getClass()) 21 // Inline conditional is OK here CHECKSTYLE:OFF 22 ? ((UploadContext) ctx).contentLength() 23 : contentLengthInt; 24 // CHECKSTYLE:ON 25 26 if (sizeMax >= 0) { 27 if (requestSize != -1 && requestSize > sizeMax) { 28 throw new SizeLimitExceededException( 29 format("the request was rejected because its size (%s) exceeds the configured maximum (%s)", 30 Long.valueOf(requestSize), Long.valueOf(sizeMax)), 31 requestSize, sizeMax); 32 } 33 input = new LimitedInputStream(input, sizeMax) { 34 @Override 35 protected void raiseError(long pSizeMax, long pCount) 36 throws IOException { 37 FileUploadException ex = new SizeLimitExceededException( 38 format("the request was rejected because its size (%s) exceeds the configured maximum (%s)", 39 Long.valueOf(pCount), Long.valueOf(pSizeMax)), 40 pCount, pSizeMax); 41 throw new FileUploadIOException(ex); 42 } 43 }; 44 } 45 46 String charEncoding = headerEncoding; 47 if (charEncoding == null) { 48 charEncoding = ctx.getCharacterEncoding(); 49 } 50 51 boundary = getBoundary(contentType); 52 if (boundary == null) { 53 throw new FileUploadException("the request was rejected because no multipart boundary was found"); 54 } 55 56 notifier = new MultipartStream.ProgressNotifier(listener, requestSize); 57 try { 58 multi = new MultipartStream(input, boundary, notifier); 59 } catch (IllegalArgumentException iae) { 60 throw new InvalidContentTypeException( 61 format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae); 62 } 63 multi.setHeaderEncoding(charEncoding); 64 65 skipPreamble = true; 66 findNextItem(); 67 }
1 private boolean findNextItem() throws IOException { 2 if (eof) { 3 return false; 4 } 5 if (currentItem != null) { 6 currentItem.close(); 7 currentItem = null; 8 } 9 for (;;) { 10 boolean nextPart; 11 if (skipPreamble) { 12 nextPart = multi.skipPreamble(); 13 } else { 14 nextPart = multi.readBoundary(); 15 } 16 if (!nextPart) { 17 if (currentFieldName == null) { 18 // Outer multipart terminated -> No more data 19 eof = true; 20 return false; 21 } 22 // Inner multipart terminated -> Return to parsing the outer 23 multi.setBoundary(boundary); 24 currentFieldName = null; 25 continue; 26 } 27 FileItemHeaders headers = getParsedHeaders(multi.readHeaders()); 28 if (currentFieldName == null) { 29 // We're parsing the outer multipart 30 String fieldName = getFieldName(headers); 31 if (fieldName != null) { 32 String subContentType = headers.getHeader(CONTENT_TYPE); 33 if (subContentType != null 34 && subContentType.toLowerCase(Locale.ENGLISH) 35 .startsWith(MULTIPART_MIXED)) { 36 currentFieldName = fieldName; 37 // Multiple files associated with this field name 38 byte[] subBoundary = getBoundary(subContentType); 39 multi.setBoundary(subBoundary); 40 skipPreamble = true; 41 continue; 42 } 43 String fileName = getFileName(headers); 44 currentItem = new FileItemStreamImpl(fileName, 45 fieldName, headers.getHeader(CONTENT_TYPE), 46 fileName == null, getContentLength(headers)); 47 currentItem.setHeaders(headers); 48 notifier.noteItem(); 49 itemValid = true; 50 return true; 51 } 52 } else { 53 String fileName = getFileName(headers); 54 if (fileName != null) { 55 currentItem = new FileItemStreamImpl(fileName, 56 currentFieldName, 57 headers.getHeader(CONTENT_TYPE), 58 false, getContentLength(headers)); 59 currentItem.setHeaders(headers); 60 notifier.noteItem(); 61 itemValid = true; 62 return true; 63 } 64 } 65 multi.discardBodyData(); 66 } 67 } 68 69 private long getContentLength(FileItemHeaders pHeaders) { 70 try { 71 return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH)); 72 } catch (Exception e) { 73 return -1; 74 } 75 } 76 77 /** 78 * Returns, whether another instance of {@link FileItemStream} 79 * is available. 80 * 81 * @throws FileUploadException Parsing or processing the 82 * file item failed. 83 * @throws IOException Reading the file item failed. 84 * @return True, if one or more additional file items 85 * are available, otherwise false. 86 */ 87 public boolean hasNext() throws FileUploadException, IOException { 88 if (eof) { 89 return false; 90 } 91 if (itemValid) { 92 return true; 93 } 94 try { 95 return findNextItem(); 96 } catch (FileUploadIOException e) { 97 // unwrap encapsulated SizeException 98 throw (FileUploadException) e.getCause(); 99 } 100 } 101 102 /** 103 * Returns the next available {@link FileItemStream}. 104 * 105 * @throws java.util.NoSuchElementException No more items are 106 * available. Use {@link #hasNext()} to prevent this exception. 107 * @throws FileUploadException Parsing or processing the 108 * file item failed. 109 * @throws IOException Reading the file item failed. 110 * @return FileItemStream instance, which provides 111 * access to the next file item. 112 */ 113 public FileItemStream next() throws FileUploadException, IOException { 114 if (eof || (!itemValid && !hasNext())) { 115 throw new NoSuchElementException(); 116 } 117 itemValid = false; 118 return currentItem; 119 }