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

Commons FileUpload的更多相关文章

  1. CVE-2014-0050: Exploit with Boundaries, Loops without Boundaries、Apache Commons FileUpload and Apache Tomcat DoS

    catalog . Description . Analysis . POC . Solution 1. Description MultipartStream.java in Apache Comm ...

  2. Apache Commons fileUpload实现文件上传之一

      需要两个jar包: commons-fileupload.jar Commons IO的jar包(本文使用commons-io-2.4.jar) 利用Servlet来实现文件上传. package ...

  3. 上传文件出错:org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. Stream ended unexpectedly

    最近做一个web项目中有上传文件的功能,已经写出并在本地和部署到服务器上测试了好几个文件上传都没问题(我用的是tomcat).后来又上传了一个700多K的文件(前边的都是不足600K的,并且这个wor ...

  4. Spring MVC使用commons fileupload实现文件上传功能

    通过Maven建立Spring MVC项目,引入了Spring相关jar依赖. 1.为了使用commons fileupload组件,需要在pom.xml中添加依赖: <properties&g ...

  5. commons.fileupload简单应用

    导入包: commons-fileupload-1.3.1.jar commons-io-2.4.jar commons-fileupload依赖于commons-io,commons-io-2.4必 ...

  6. JSP 文件上传下载系列之二[Commons fileUpload]

    前言 关于JSP 文件上传的基础和原理在系列一中有介绍到. 这里介绍一个很流行的组件commons fileupload,用来加速文件上传的开发. 官方的介绍是:  让添加强壮,高性能的文件到你的se ...

  7. Caused by: java.lang.ClassNotFoundException: org.apache.commons.fileupload.RequestContext

    1.错误描述 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help ...

  8. org.apache.commons.fileupload.FileUploadBase$InvalidContentTypeException

    1.错误原因 org.apache.commons.fileupload.FileUploadBase$InvalidContentTypeException: the request doesn't ...

  9. 上传文件代码报错,java.lang.ClassNotFoundException: org.apache.commons.fileupload.FileItemFactory

    2018-09-11 11:11:08.235 ERROR 14352 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : ...

  10. Apache Commons FileUpload 实现文件上传

    Commons FileUpload简介 Apache Commons是一个专注于可重用Java组件开发的 Apache 项目.Apache Commons项目由三个部分组成: 1.Commons P ...

随机推荐

  1. Windows MFC 打开文本

    MFC的CFileDialog自动封装了文件相关的对话框,提供一种简单的文件打开和文件存盘对话框功能. 要使用CFileDialog类,首先要构造一个对象, 项目实例: CFileDialog fil ...

  2. flume jetty 进程关系 flume jetty 跨域问题 jetty 源码分析

    flume  jetty  跨域问题 13481 httpSource的端口进程号 = flume 启动后的进程号 [root@c log]# netstat -atp Active Internet ...

  3. 并发编程 - 进程 - 1.互斥锁/2.模拟抢票/3.互斥锁与join区别

    1.互斥锁: 互斥锁:Lock 原理就是把并发变成串行,一个一个运行,不错乱,但效率低 保证多个进程修改一块数据时,大家是一个一个修改,不错乱 mutex.acquire() mutex.releas ...

  4. servlet实现多文件打包下载

    当用户一次下载多个文件时.普通情况是,每下载一个文件,均要弹出一个下载的对话框.这给用户造成了非常大不便. 比較理想的情况是,用户选择多个文件后.server后端直接将多个文件打包为zip.以下贴出实 ...

  5. 通过EditText的setTransformationMethod()隐藏或显示密码

    private Button mBtnPassword; private EditText mEtPassword; private boolean mbDisplayFlg = false; /** ...

  6. Zabbix server(离线版)安装手册

    由于zabbix server需要依赖MySQL及PHP的相关依赖,因此需要先安装好MySQL及PHP的相关依赖后方可安装zabbixserver. 安装MySQL 目录mysql下的rpm 1.新建 ...

  7. dijstra算法,求源点到各个顶点的最短距离

    1:dijstra算法常用语求最短距离, dijstra每次从未发现节点n[]中,发现距离源点最短的节点m,求出最短节点后,将m添加到已发现节点y[]中,用该节点m进行更新其它未发现节点n[]-m的最 ...

  8. C#生成电子印章源码

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  9. sql server 复制表从一个数据库到另一个数据库

    sql server 复制表从一个数据库到另一个数据库 /*不同服务器数据库之间的数据操作*/ --创建链接服务器 exec sp_addlinkedserver 'ITSV ', ' ', 'SQL ...

  10. Python 开发面试总结

    网络基础 如何确定发送过来的数据的完整性(有无中间人攻击)? 散列值校验(MD5.SHA-1).数字签名(PGP),需要用户亲自校验,若是散列值或数字签名本身被篡改,用户是无法判断出来的. HTTPS ...