zoukankan      html  css  js  c++  java
  • Commons FileUpload

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

  • 相关阅读:
    [leetcode]Longest Common Prefix
    [leetcode]Container With Most Water
    [leetcode]Regular Expression Matching
    [leetcode]Palindrome Number
    [leetcode]String to Integer (atoi)
    [leetcode]Reverse Integer
    [leetcode]ZigZag Conversion
    [leetcode]Longest Palindromic Substring
    [leetcode]Add Two Numbers
    poj 1228 Grandpa's Estate
  • 原文地址:https://www.cnblogs.com/chenkeyu/p/8592158.html
Copyright © 2011-2022 走看看