转载自(https://my.oschina.net/u/2000201/blog/486744)

1    概述

Commons FileUpdate包很容易为你的Servlet和web应用程序添加健壮的、高性能的文件上传功能。
FileUpload解析遵循RFC 1876(在HTML中基于表单的文件上传)HTTP请求。即,如果一个HTTP请求使用POST方法提交,并
且使用“multipart/form-data”的内容类型,然后FileUpload解析请求,使结果易于调用者使用。
从1.3开始,FileUpload处理RFC 2047编码头值。

2    用户指南

2.1    使用FileUpload

FileUpload能使用大量不同的方式,依赖于你的应用程序的需求。在简单的情况下,你将调用简单的方法解析Servlet请求,
然后处理item列表作为它们应用到你的应用程序。在天平的另一端,你可能决定自定义FileUpload充分的控制单个item存储
的方式;例如,你可能决定将流的内容写入数据库。
这里,我们将描述FileUpload的基本原则,并阐述一些更简单的——并且更通用的——使用模式。
FileUpload依赖于Commons IO。

2.2    工作原理

一个文件上传请求包含一个根据RFC 1867(在HTML中基于表单的文件上传)编码的有序item列表。FileUpload能解析这么一个请求,并提供给你的应用程序单独的上传item列表。每个item实现FileItem接口,不管它底层实现。
本文描述Commons FileUpload类库的传统API。传统API是便利方式。然而,对于最终性能,你可能喜欢快速的流API。
每个文件item有你的应用程序可能感兴趣的许多属性。例如,每个item有一个名字和内容类型,并提供一个InputStream访问
它的数据。换句话说,你可能需要处理不同的item,依赖于是否item是常规表单——即,数据来自原始文本框或类似于HTML字段——或上传文件。FileItem接口提供方法做出这一决定,并以最适当的方式访问数据。
FileUpload使用FileItemFactory创建新文件item。这给FileUpload更大的灵活性。工厂最终控制每个item如何创建。工厂实
现当前过渡FileUpload存储item的数据在内存或磁盘,依赖于item的大小(例如,数据的字节)。然而,该行为能自定义适合你的应用程序。

2.3    Servlet和Portlet

从1.1开始,FileUpload支持Servlet和Portlet环境的文件上传请求。在两种环境中的使用几乎相同,因此,本文只讲述
Servlet环境。
如果你构建一个Portlet应用程序,以下两点不同你应该阅读API文档:

ServletFileUpload类->PortletFileUpload类
HttpServletRequest类->ActionRequest类

2.4    解析请求

你处理上传item之前,当然,你需要解析请求。确保,请求是一个简单的真实文件上传请求,但FileUpload使其变得简单,
通过提供一个静态方法做到这一点。

// 检查,我们有一个文件上传请求
boolean isMultipart = ServletFileUpload.isMultipartContent(request);

现在,我们准备解析请求为item。

2.5    最简单的情况

以下是最简单的使用情景:

  • 上传item应该保留在内容中,只要它相当小。

  • 大型item应该写入磁盘上的临时文件。

  • 非常大的上传请求应该不被允许。

  • 内置默认的保存在内存中的一个item的最大大小,一个上传请求的最大大小,和可接受的临时文件位置。

在这种情况下,处理请求不应该更简单:

// 创建基于磁盘文件item的工厂
DiskFileItemFactory factory = new DiskFileItemFactory();

// 配置一个仓库(确保一个安全的临时位置被使用)
ServletContext servletContext = this.getServletConfig().getServletContext();
File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);

// 创建一个新的文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);

// 解析请求
List<FileItem> items = upload.parseRequest(request);

这是我们的所有需要。
解析的结果是文件item List,每个FileItem接口的实现。

2.6    练习更多控制

如果你使用的情景是上面描述的最简单的情况,但是你需要一点更多的控制,你能易于定制上传处理器或文件item工厂的行为。
以下例子显示各种配置选项:

// 创建基于磁盘文件item的工厂
DiskFileItemFactory factory = new DiskFileItemFactory();

// 设置工厂约束
factory.setSizeThreshold(yourMaxMemorySize);
factory.setRepository(yourTempDirectory);

// 创建一个新的文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);

// 设置全部请求大小约束
upload.setSizeMax(yourMaxRequestSize);

// 解析请求
List<FileItem> items = upload.parseRequest(request);

当然,每个配置方法独立于其它方法,但如果你想要同时配置工厂,你能使用构造函数,像这样:

// 创建一个基于磁盘的文item工厂
DiskFileItemFactory factory = new DiskFileItemFactory(yourMaxMemorySize, yourTempDirectory);

你应该需要在请求解析上更高级的控制,例如,在其它地方存储item——例如,在数据库中。

2.7    处理上传item

一旦解析完成,你将有一个需要处理的文件item List。在大多数情况下,你将想要处理不同于普通表单字段的文件上传,
因此你可以像这样处理:

// 处理上传item
Iterator<FileItem> iter = items.iterator();
while (iter.hasNext()) {
    FileItem item = iter.next();

if (item.isFormField()) {
        processFormField(item);
    } else {
        processUploadedFile(item);
    }
}

对于表单字段,你将只对item名称和它的String值感兴趣。正如你料想的,访问这些非常简单。

// 处理常规表单字段
if (item.isFormField()) {
    String name = item.getFieldName();
    String value = item.getString();
    ...
}

对于文件上传,有几种不同的东西,你可能会想知道你处理之前的内容。下面是一些你可能感兴趣的方法示例。

// 处理文件上传
if (!item.isFormField()) {
    String fieldName = item.getFieldName();
    String fileName = item.getName();
    String contentType = item.getContentType();
    boolean isInMemory = item.isInMemory();
    long sizeInBytes = item.getSize();
    ...
}

使用上传文件,你通常不想通过内存要访问它们,除非它们很小,或者你没有其它选择。你宁愿将想要处理的内容作为一个流,或写入整个文件到它的最终位置。FileUpload提供简单的方法完成这两种。

// 处理文件上传
if (writeToFile) {
    File uploadedFile = new File(...);
    item.write(uploadedFile);
} else {
    InputStream uploadedStream = item.getInputStream();
    ...
    uploadedStream.close();
}

注意,在默认的FileUpload实现中,如果数据已经在临时文件中,write()将试图重命名文件到指定目标。
如果你需要访问内存中的上传数据,你需要简单调用get()方法获取数据作为byte数组。

// 处理内存中的文件上传
byte[] data = item.get();
...

2.8    资源清理

如果你使用DiskFileItem,你上传的文件处理之前被写入临时文件。这些临时文件被自动删除,如果他们不再使用,如果相应的java.io.File实例被垃圾回收。通过org.apache.commons.io.FileCleaner类默默开启一个清理线程。这个清理线程应该被停止,如果它不再需要。在Servlet环境中,通过使用一个特定Servlet上下文监听器(FileCleanerCleanup)来完成。为了这么做,在你的web.xml中添加:

<web-app>
  ...
  <listener>
    <listener-class>
      org.apache.commons.fileupload.servlet.FileCleanerCleanup
    </listener-class>
  </listener>
  ...
</web-app>

2.9    创建DiskFileItemFactory

FileCleanerCleanup提供一个org.apache.commons.io.FileCleaningTracker实例。当创建org.apache.commons.fileupload.disk.DiskFileItemFactory时必须使用该实例。

public static DiskFileItemFactory newDiskFileItemFactory(ServletContext context, File repository) {
    FileCleaningTracker fileCleaningTracker = FileCleanerCleanup.getFileCleaningTracker(context);
    DiskFileItemFactory factory = new DiskFileItemFactory(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD, repository);
    factory.setFileCleaningTracker(fileCleaningTracker);
    return factory;
}

2.10    禁用临时文件清理

为了禁用临时文件跟踪,你可以设置FileCleaningTracker为null。因此,创建文件将不再被跟踪。尤其,它们将不再被自动删除。

2.11    集成病毒扫描

病毒扫描和web容器运行在相同的系统中对于使用FileUpload的应用程序会导致一些意想不到的行为。本文描述一些你可能遇到的行为,并且提供一些如何处理它们的方式。
FileUpload的默认实现将导致上传的item超过某一阀值被写入磁盘。就这样一个文件关闭,系统上的任何病毒扫描程序会来检查它,并且可能隔离文件——即,把它移动到一个不能导致文件的地方。当然,这将给应用程序开发人员一个惊喜,因为上传文件item不再有效。话句话说,上传item在相同阀值下将保存在内容中,因此将不会被病毒扫描器发觉。这允许一个病毒可能以某种形式被保留(尽管从不写入磁盘,病毒扫描程序应该定位和检查它)。
一个常用的解决办法是将所有上传的文件放置系统的一个目录中,并配置病毒扫描器忽略该目录。这将确保应用程序不会丢掉文件,然而脱离病毒扫描程序的职责范围,对上传的文件进行病毒扫描可以由外部处理,移动干净或以清理了文件到“批准”位置,或在应用程序中集成病毒扫描程序。

2.12    查看进程

如果你期望真实的大文件上传,那么它将很好的报告给你的用户已经接收了多少。每个HTML页面允许实现一个进度条,通过返回一个multipart/replace响应或这样的东西。

查看上传进度可以通过提供一个进度监听器完成:

// 创建一个进度监听器
ProgressListener progressListener = new ProgressListener(){
   public void update(long pBytesRead, long pContentLength, int pItems) {
       System.out.println("We are currently reading item " + pItems);
       if (pContentLength == -1) {
           System.out.println("So far, " + pBytesRead + " bytes have been read.");
       } else {
           System.out.println("So far, " + pBytesRead + " of " + pContentLength
                              + " bytes have been read.");
       }
   }
};
upload.setProgressListener(progressListener);

帮自己一个忙,实现你的第一个进度监听器,像上面这样,因为它显示了一个陷阱:进度监听器被频繁调用。依赖于Servlet引擎和其它环境工厂,它可能调用任何网络包!换句话说,你的进程监听器可能成为性能问题!一个典型的解决方法是减少进度监听器的活跃度。例如,你可以只在兆字节数量改变时发出消息:

// 创建进度监听器
ProgressListener progressListener = new ProgressListener(){
   private long megaBytes = -1;
   public void update(long pBytesRead, long pContentLength, int pItems) {
       long mBytes = pBytesRead / 1000000;
       if (megaBytes == mBytes) {
           return;
       }
       megaBytes = mBytes;
       System.out.println("We are currently reading item " + pItems);
       if (pContentLength == -1) {
           System.out.println("So far, " + pBytesRead + " bytes have been read.");
       } else {
           System.out.println("So far, " + pBytesRead + " of " + pContentLength
                              + " bytes have been read.");
       }
   }
};

3    流API

3.1    为什么使用流?

假设,文件item实际被用户访问之前必须存储在某一地方。这种方式很方便,因为它允许易于访问item内容。话句话说,
它消耗内存和时间。
流API允许你牺牲一点点便利,优化性能和较低的内存配置。然而,流API更轻量级,因此易于理解。

3.2    工作原理

FileUpload类用于访问表单字段和字段顺序,它们已经通过客户端发送。然而,FileItemFactory完全被忽略。

3.3    解析请求

首先,不要忘记确保,请求实际是一个文件上传请求。这通常是通过使用相同的静态方法。

// 检查我们有一个文件上传请求
boolean isMultipart = ServletFileUpload.isMultipartContent(request);

现在我们已经准备好解析请求:

// 创建新文件上传处理器
ServletFileUpload upload = new ServletFileUpload();

// 解析请求
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
    FileItemStream item = iter.next();
    String name = item.getFieldName();
    InputStream stream = item.openStream();
    if (item.isFormField()) {
        System.out.println("Form field " + name + " with value "
            + Streams.asString(stream) + " detected.");
    } else {
        System.out.println("File field " + name + " with file name "
            + item.getName() + " detected.");
        // 处理输入流
        ...
    }
}

最新文章

  1. google protobuf安装与使用
  2. Dll的生成,转化为OMF格式的DLL
  3. Mysql监控、优化
  4. Java面试(3)-- Java关系运算符
  5. Koa框架实践与中间件原理剖析
  6. 德飞莱STM32单片机学习(一)&mdash;&mdash;下载环境搭建
  7. redis pipeline
  8. hdu 5150 Sum Sum Sum 水
  9. ERP系统实施与企业内部控制管理实践
  10. Android:ListView之ViewHolder
  11. HeadFirst设计模式之门面模式
  12. php和cookie
  13. 简单粗暴的在vmware虚拟机中固定ip
  14. mysql 压缩版配置
  15. spring boot整合websocket
  16. oracle 数据库去重复数据
  17. HDU 6077 17多校4 Time To Get Up 水题
  18. 小记SharePoint REST API Search和COM
  19. .NET零基础入门05:委托与事件
  20. 5 -- Hibernate的基本用法 --1 ORM和Hibernate

热门文章

  1. HDU4291—A Short problem
  2. HTTP Transaction Delays
  3. jsp-&gt;jar
  4. cross-compler toolchains--clfs
  5. csv的文件excel打开长数字后面位变0的解决方法
  6. librosa音频特征提取,python librosa库在centos上依赖llvm的问题?
  7. Period---hdu1358(循环节 kmp)
  8. &amp;lt;Android 开源库&amp;gt; GreenDAO 使用方法具体解释&amp;lt;译文&amp;gt;
  9. (3.15)常用知识-sql server分页
  10. Jmeter+jenkins如何快速搭建接口和性能测试持续集成解决方案-[基于windows篇]