java FileUpload 组件上传文件

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         }
View Code

 

posted @ 2014-11-24 14:37  act262  阅读(1119)  评论(0编辑  收藏  举报