Commons FileUpload

1    概述

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

2    用户指南

2.1    使用FileUpload

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

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文档:apache

ServletFileUpload类->PortletFileUpload类
HttpServletRequest类->ActionRequest类数组

2.4    解析请求

你处理上传item以前,固然,你须要解析请求。确保,请求是一个简单的真实文件上传请求,但FileUpload使其变得简单,
经过提供一个静态方法作到这一点。安全

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

如今,咱们准备解析请求为item。
app

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.");        // 处理输入流        ...    }}

相关文章
相关标签/搜索