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

        ...

    }

}


 

 

你可能感兴趣的:(fileupload)