zoukankan      html  css  js  c++  java
  • JSP 文件上传下载系列之二[Commons fileUpload]

    前言

    关于JSP 文件上传的基础和原理在系列一中有介绍到。 这里介绍一个很流行的组件commons fileupload,用来加速文件上传的开发。

    官方的介绍是:  让添加强壮,高性能的文件servlet和Web应用程序变得容易。

    官方项目地址:

    http://commons.apache.org/proper/commons-fileupload/

    FileUpload分析request 里的数据,  生成一些独立的上传items. 每一个item都继承自 FileItem 这个接口。

    下载导入

    1. 可以到 http://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi 这个地址下载最新的版本。

    2. 另外还需要下载 commons-io的 jar 包,下载地址:

    http://commons.apache.org/proper/commons-fileupload/dependencies.html

    在servlet 和portlet中都可以使用FileUpload, 以下以servlet的使用来介绍

    分析请求(request)

    首先,需要判断request 是否是文件上传的request.

    系列一也有提,文件上传的form 必须设置成如下:

    <form method="POST" enctype="multipart/form-data" action="fileUploadServlet">

    这里有提供一个方法判断request 是否是正确的类型

    // Check that we have a file upload request
    boolean isMultipart = ServletFileUpload.isMultipartContent(request);

    例如这里, 如果form 中移除enctype="multipart/form-data", 返回值就是false 了。

    最简单的状况

    最简单的使用场景如下:

    1. 如果上传的文件足够小的话应该保存在内存中

    2. 大的文件应该写到临时文件中

    3. 超大的文件上传请求应该不被允许

    4. 内存中的文件最大值,允许上传的文件最大尺寸和临时文件目录的接收默认的设置。

    看个实例:

    // Create a factory for disk-based file items
    FileItemFactory factory = new DiskFileItemFactory();
    
    // Configure a repository (to ensure a secure temp location is used)
    ServletContext servletContext = this.getServletConfig().getServletContext();
    File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
    factory.setRepository(repository);
    
    // Create a new file upload handler
    ServletFileUpload upload = new ServletFileUpload(factory);
    
    // Parse the request
    List<FileItem> items = upload.parseRequest(request);


    更多的控制

    也可以进行更多的设置, 看例子

    // Create a factory for disk-based file items
    DiskFileItemFactory factory = new DiskFileItemFactory();
    
    // Set factory constraints
    factory.setSizeThreshold(yourMaxMemorySize);
    factory.setRepository(yourTempDirectory);
    
    // Create a new file upload handler
    ServletFileUpload upload = new ServletFileUpload(factory);
    
    // Set overall request size constraint
    upload.setSizeMax(yourMaxRequestSize);
    
    // Parse the request
    List<FileItem> items = upload.parseRequest(request);


    这里设置了最大内存大小,临时文件路径和文件最大值。

    设置方式也可以这样:

    DiskFileItemFactory factory =newDiskFileItemFactory(yourMaxMemorySize, yourTempDirectory);

    处理上传的item

    需要再提一下的是: FileUpload 不仅会把一个file 的input 放入一个 FileItem, 一般的Form text input 也会放入一个FileItem.

    所以在分析完之后接下来就是如何处理这些FileItem 了。

    // Process the uploaded items
    Iterator<FileItem> iter = items.iterator();
    while (iter.hasNext()) {
        FileItem item = iter.next();
    
        if (item.isFormField()) {
            processFormField(item);
        } else {
            processUploadedFile(item);
        }
    }
    
    

    对于一般的form field 来说(text input), 无非就是取它的name 和value  了。

    // Process a regular form field
    if (item.isFormField()) {
        String name = item.getFieldName();
        String value = item.getString();
        ...
    }

    对于文件类型的话, 可以

    // Process a file uploadif(!item.isFormField()){
        String fieldName = item.getFieldName();
        String fileName = item.getName();
        String contentType = item.getContentType();
        boolean isInMemory = item.isInMemory();
        long sizeInBytes = item.getSize();
        ...}

    看是否需要写入到某个文件中:

    // Process a file uploadif(writeToFile){
        File uploadedFile =newFile(...);
        item.write(uploadedFile);}else{
        InputStream uploadedStream = item.getInputStream();
        ...
        uploadedStream.close();}

    得到文件

    // Process a file upload in memorybyte[] data = item.get();...

    资源清除

    如果使用DiskFileItem, 或者说在处理上传文件之前写入临时文件的话,就要考虑资源清除了。

    临时文件不再使用的话,是会自动被删除的。org.apache.commons.io.FileCleaner 这个类会启动一个回收线程。

    如果不再需要这个回收线程的话, 可以停止它。 方法是在xml 中加入:

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

     

    创建一个 DiskFileItemFactory

    FileCleanerCleanup提供了一个org.apache.commons.io.FileCleaningTracker的实例,如果创建一个org.apache.commons.fileupload.disk.DiskFileItemFactory则需要这个实例。像

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

    如果不想删除临时文件的话, 设置 FileCleaningTracker为null 就可以了。 因此,创建的文件将不再被跟踪。特别是,它们不再被自动删除

    与病毒扫描软件的问题

    病毒扫描软件可能会导致FileUpload 的一些异常状况。

    解决方式就是让扫描软件不要监视某些特定的目录。

    查看上传进度

    //Create a progress listenerProgressListener progressListener =newProgressListener(){
       publicvoid 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);

    或者

    //Create a progress listenerProgressListener progressListener =newProgressListener(){
       privatelong megaBytes =-1;
       publicvoid 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.");
           }
       }};

    Streaming API

    上面提到的API (传统API) 是在使用前完全把Item  读到某个地方(内存或是文件),使用Streaming 的话,可以逐步的读取, 性能和内存使用都会大大提升。

    // Create a new file upload handler
    ServletFileUpload upload = new ServletFileUpload();
    
    // Parse the request
    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.");
            // Process the input stream
            ...
        }
    }


  • 相关阅读:
    嵌入式Linux基础知识
    面向对象程序设计与原则
    设计模式之工厂模式
    设计模式之单例模式
    基于Doxygen的C/C++注释原则
    设计模式概论
    重启博客
    java 单例模式的几种写法
    CountDownLatch、CyclicBarrier
    java ConcurrentHashMap和CopyOnWriteArrayList解决并发问题
  • 原文地址:https://www.cnblogs.com/pangblog/p/3329096.html
Copyright © 2011-2022 走看看