`
feng88724
  • 浏览: 172783 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

Struts 文件上传 FormFile与Commons-FileUpload控件

阅读更多
前一段时间刚来公司,看到一个项目中以前有人写的struts代码。是使用了FormFile来处理关于文件上传的模块。但是用力一段时间后,发现出问题了。写完的这个模块,上传文件是没有问题的,但是当服务器的空间较小的时候,穿一个比较大的文件就出问题了,文件还没有上传完,就抛出一个错误的页面,报告上传模块出了问题,而且是Tomcat默认的出错页面。

    于是想办法,修改,查看源代码,发现原来写这段代码的人是默认等文件上传完以后进入Action了才判断文件大小是否超出了限制。

    但是,默认配置下使用struts的FormFile比较特殊,FormFile是struts包对外的一个接口,而且org.apache.struts.upload包是使用的commons-fileupload-1.0进行的封装。如果使用了它来实现文件上传的功能,则必须是FormFile对象在被初始化以后才能使用,那什么时候它才是被初始化的呢?

    答案是:在进入Action之前就已经初始化好了!

因此,原先的设计:在Action中判断文件大小是根本不能在上传过程中起到提示作用的,因为这时候文件已经上传完了。而且这个设计还有一个确定就是不能捕获上传过程中出现的任何问题。也就是说:在Action里我们得到的FormFile对象是上传的一个结果,而不是一个未上传好就可以使用的对象!

    那如何控制FormFile上传的过程呢?显然,在Action里处理已经不能奏效了,想想别的办法,让我们翻看一下Struts的源代码找找灵感吧。

    这是struts1.1的org.apache.struts.upload包的描述:
    (见附件)
    从上图我们可以看出有有CommonsMultipartRequestHandler和DiskMultipartRequestHandler两个类实现了MultipartRequestHandler接口。

    大家都知道,Commons-fileupload控件在上传的时候,使用的enctype为:enctype="multipart/form-data",因此不难看出MultipartRequestHandler的实现就是来处理enctype="multipart/form-data"这样的post请求的。

    但是这里有两个类,CommonsMultipartRequestHandler和DiskMultipartRequestHandler。到底哪个是处理FormFile的上传的呢?这个问题应该从org.apache.struts.config包里来找。
   
    org.apache.struts.config包是用来处理struts配置文件的数据的包。找到org.apache.struts.config. ControllerConfig。

    看这几行:
/**       
* The fully qualified Java class name of the MultipartRequestHandler       
* class to be used.       
*/         
protected String multipartClass =    
"org.apache.struts.upload.CommonsMultipartRequestHandler";      
        
public String getMultipartClass()    
{             
     return (this.multipartClass);         
}          
   
public void setMultipartClass(String multipartClass)    
{             
     if (configured)    
      {                 
           throw new IllegalStateException("Configuration is frozen");                      
      }      
            
     this.multipartClass = multipartClass;        
}     

这几行的意思很明白,如果没有在配置文件中配置MultipartRequestHandler实现类的绝对路径,那就使用org.apache.struts.upload.CommonsMultipartRequestHandler类默认处理。

   ^_^,这就是关键了:struts是默认使用org.apache.struts.upload.CommonsMultipartRequestHandler类来处理FormFile指定的上传文件的。

   马上转到org.apache.struts.upload.CommonsMultipartRequestHandler来看看:
/**     
*默认文件上传的大小是250M     
*/        
public static final long DEFAULT_SIZE_MAX = 250 * 1024 * 1024;         
/**     
*上传文件在内存中使用的缓冲区大小,超过次数值的数据写入硬盘。     
*/        
public static final int DEFAULT_SIZE_THRESHOLD = 256 * 1024;    

还有,最最重要的实现方法:
**     
* Parses the input stream and partitions the parsed items into a set of     
* form fields and a set of file items. In the process, the parsed items     
* are translated from Commons FileUpload <code>FileItem</code> instances     
* to Struts <code>FormFile</code> instances. 
* @param request The multipart request to be processed.   
* @throws ServletException if an unrecoverable error occurs.                
就是这个函数处理上传文件的request,把request交给Commons FileUpload 控件处理,并解析FileItem转换成Struts的FormFile。     
*/   
public void handleRequest(HttpServletRequest request) throws ServletException  

再看看这个函数内部是怎么实现的吧?
// 使用了DiskFileUpload。      
// (Commons-FileUpload很老版本的一个上传实现类了,还在用?我的显示是Deprecated)        
DiskFileUpload upload = new DiskFileUpload();        
// 上传最大值        
upload.setSizeMax((int) getSizeMax(ac));        
// 上传文件在内存中使用的缓冲区大小        
upload.setSizeThreshold((int) getSizeThreshold(ac));        
// 存在硬盘的什么地方,一般是默认    
pload.setRepositoryPath(getRepositoryPath(ac));     

接着看handleRequest如何处理request的:
// Parse the request into file items.                
List items = null;                
try    
{                    
      items = upload.parseRequest(request);                
}    
//这里是关键:上传过程中出了超出最大值的异常了,如何处理?          
catch (DiskFileUpload.SizeLimitExceededException e)    
{                     
     // Special handling for uploads that are too big          
      request.setAttribute(    
MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED,    
            Boolean.TRUE);                    
     return;                
}                     
//出了其他异常,如enctype不对,磁盘空间不足怎么办?        
catch (FileUploadException e)    
{                    
      log.error("Failed to parse multipart request", e);                    
     throw new ServletException(e);                
}    
这次一目了然了:

   Struts根本没有把上传过程中出的超出最大值的异常带到Action,而是把它放到了rquest的Attribute里。
   而出了其他异常如enctype不对,磁盘空间不足怎么办?很遗憾,Struts没有去处理它,而是log了一下,抛给了上一层了。

   那我一定要获得这些全部异常咋办呢?没办法,自己定制一个MultipartRequestHandler吧,那样就能彻底解决上传过程中的控制问题了!

   在此之前,我们得先去最新版的commons-fileupload控件看看上传过程中可能抛出多少异常?
//所有上传异常的父类    
org.apache.commons.fileupload.FileUploadException    
//注意:这个类的类名是FileUploadBase.SizeLimitExceededException是个public内    
//部类。上传的formdata总的数据超出了规定大小    
org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException    
//注意:也是个内部类。这个才是上传的文件超出了规定大小    
org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException    
//其它的,也看看吧:    
org.apache.commons.fileupload.FileUploadBase.FileUploadIOException    
org.apache.commons.fileupload.FileUploadBase.InvalidContentTypeException    
org.apache.commons.fileupload.FileUploadBase.IOFileUploadException    
org.apache.commons.fileupload.FileUploadBase.UnknownSizeException 

要想获得尽可能仔细的数据就在处理的try/catch块里把上面的异常都catch一下,放到request的attribute里去就OK了。

    另外还有要说的是,最好用commons-fileupload控件的最新版本,因为DiskFileUpload这个类,commons-fileupload已经弃用了,取而代之的是ServletFileUpload类了,所以一定要注意!切记,切记…..

    这是我写的CommonsMultipartRequestHandler替代类的public void handleRequest(HttpServletRequest request) throws ServletException函数:
public void handleRequest(HttpServletRequest request) throws ServletException
{
     // Get the app config for the current request.
     ModuleConfig ac = (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);

     // DiskFileItem工厂,主要用来设定上传文件的参数
     DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();

     // 上传文件所用到的缓冲区大小,超过此缓冲区的部分将被写入到磁盘
     fileItemFactory.setSizeThreshold((int) this.getSizeThreshold(ac));

     // 上传文件用到的临时文件存放位置
     fileItemFactory.setRepository(this.getRepository(ac));

     // 使用fileItemFactory为参数实例化一个ServletFileUpload对象
     // 注意:该对象为commons-fileupload-1.2新增的类.
     // 对于1.2以下的commons-fileupload版本并不存在此类.
     ServletFileUpload upload = new ServletFileUpload(fileItemFactory);

     // 从session中读取对本次上传文件的最大值的限制
     String maxUploadSize = (String)request.getSession().
getAttribute(BasicConstants.maxUploadSize);

     // 获取struts-config文件中controller标签的maxFileSize属性来确定默认上传的限制
     // 如果struts-config文件中controller标签的maxFileSize属性没设置则使用默认的上传限制250M.
     long defaultOrConfigedMaxUploadSize = this.getSizeMax(ac);
     if (maxUploadSize != null && maxUploadSize != "")
     {
          // 如果maxUploadSize设定不正确则上传限制为defaultOrConfigedMaxUploadSize的值

          // 正确则为maxUploadSize转换成的字节数
          upload.setSizeMax((long) this.convertSizeToBytes(
maxUploadSize, defaultOrConfigedMaxUploadSize));
      
      }
      else
      {
          // 如果maxUploadSize没设置则使用默认的上传限制 
          upload.setSizeMax(defaultOrConfigedMaxUploadSize);
      } 

      // 从session中清空maxUploadSize
      request.getSession().removeAttribute("maxUploadSize"); 

      // Create the hash tables to be populated.
      elementsText = new Hashtable();
      elementsFile = new Hashtable();
      elementsAll = new Hashtable(); 

      // Parse the request into file items.
      List items = null;
      // ServletFileUpload类来处理表单请求
      // 抛出的异常为FileUploadException的子异常
      // 如果捕获这些异常就将捕获的异常放到session中返回.

      try
      {
            items = upload.parseRequest(request);

      }
      catch (FileUploadBase.SizeLimitExceededException e)
      {

            // 请求数据的size超出了规定的大小.
            request.getSession().setAttribute(
                BasicConstants.baseSizeLimitExceededException, e);
            return;
      }
      catch (FileUploadBase.FileSizeLimitExceededException e)
      {
            // 请求文件的size超出了规定的大小.
            request.getSession().setAttribute(
                BasicConstants.baseFileSizeLimitExceededException, e);
            return;
      }
      catch (FileUploadBase.IOFileUploadException e)
      {
            // 文件传输出现错误,例如磁盘空间不足等.
            request.getSession().setAttribute(
                BasicConstants.baseIOFileUploadException, e);
            return;
      }
      catch (FileUploadBase.InvalidContentTypeException e)
      {
            // 无效的请求类型,即请求类型enctype != "multipart/form-data"
            request.getSession().setAttribute(
                BasicConstants.baseInvalidContentTypeException, e);
            return;
      }
      catch (FileUploadException e)
      {
           // 如果都不是以上子异常,则抛出此总的异常,出现此异常原因无法说明.
           request.getSession().setAttribute(
                BasicConstants.FileUploadException, e);
           return;
      }

      // Partition the items into form fields and files.
      Iterator iter = items.iterator();
      
      while (iter.hasNext())
      {
            FileItem item = (FileItem) iter.next();

            if (item.isFormField())
            {
                   addTextParameter(request, item);
            }
            else
            {
                   addFileParameter(item);
            }
      }
}

其它部分均未做什么大改变。

好了,替代类写好了,我们怎么去用呢?

这样:在struts-config文件中写配置:
<!-- ========== Controller Configuration ================================ -->

       <controller>

              <!-- The "input" parameter on "action" elements is the name of a

                     local or global "forward" rather than a module-relative path -->

              <set-property value="true" property="inputForward" />
              <set-property value="text/html; charset=UTF-8"
                     property="contentType" />
              <!-- 通过写类的全名来替代struts默认的MultipartRequestHandler -->             

              <set-property property="multipartClass" 
value="com.amplesky.commonmodule.struts.AmpleskyMultipartRequestHandler" />
              <!-- 规定的上传文件的最大值 -->
              <set-property property="maxFileSize" value="15M" />
              <!--  缓冲区大小 -->
              <set-property property="memFileSize" value="5M" />
       </controller>

好了!现在我们再用FormFile上传文件,可以在上传之前动态设置或者从配置文件设置上传文件的大小,一旦上传过程中出现了异常,就会被写入request的attributs里。当进入action的时候,通过在Action里获取异常就可以判断上传过程中出了什么问题了,而且在上传过程中文件一旦超出了规定大小,或者磁盘大小不足的情况会立即中断上传的。这样我们的功能就实现了。

最后,感慨一下,

1.感觉commons-fileupload还是挺好用的,但是FormFile的使用不大好,基本上误导能力很强,网上关于FormFile的资料说明很少,以上这些都是我自己摸索出来的。

2.Struts 1都到了1.3版本了,但是对于FormFile的实现依然使用commons-fileupload-1.0版本的DiskFileUpload类,可见更新也不怎么样。其实我觉得这是apache大部分开源工具的一个通病:更新确实不怎么快,commons框架里边的源代码和jar包不一致,还有很多是2004或者2003年的…要命的是居然不支持javase5的新特性????

3. 认为把异常放到request的attributs里不好,曾经尝试过抛出异常然后通过配置exception-controller来抓去异常处理,但是抓不到,怎么都抓不到,后来翻了大半天源码才发现:struts在处理异常请求的时候将出现的ServletException和IOExcepton都交给了上层去处理了,根本不会抛出来。所以这两种异常是抓不到的。


转自:【http://www.iteye.com/topic/212566#】
分享到:
评论

相关推荐

    (转)Struts的FormFile与Commons-FileUpload控件使用心得

    Struts的FormFile与Commons-FileUpload控件使用心得。

    struts文件上传JAR包(upload)

    在Struts中集成`commons-fileupload`,你需要在项目的类路径中包含这个JAR包,并且配置Struts的ActionForm或自定义的Action来处理文件上传。例如,你需要创建一个继承自`org.apache.struts.upload.FormFile`的类来...

    自定义Controller的multipartClass类实现Struts上传

    Struts的FormFile与Commons-FileUpload控件使用心得 详细内容可以参考http://blog.csdn.net/lowkeysk/article/details/8296289

    struts文件上传.pdf

    然后,确保项目中包含了Struts框架所需的库文件,特别是用于文件上传的`commons-fileupload.jar`。 2. **创建ActionForm** 创建一个ActionForm类用于封装表单数据。如果需要上传文件,该类中的相应属性应该声明为...

    struts框架中formFile批量上传图片

    在Struts框架中,`FormFile`类是用于处理文件上传的一个关键组件,特别是对于单个或批量上传图片的情况。本篇文章将详细探讨如何使用Struts的`FormFile`来实现批量上传图片的功能。 ### 1. FormFile概述 `FormFile`...

    struts 上传与下载

    1. **文件上传组件**: 在 Struts 中,文件上传主要依赖于 `commons-fileupload` 和 `commons-io` 这两个 Apache Commons 库。`Commons-fileupload` 提供了处理 HTTP 多部分请求的能力,而 `Commons-io` 提供了文件...

    jsp+struts文件上传

    在Struts的Action类中,我们需要使用Apache Commons FileUpload库来处理上传的文件。首先,确保你的项目已经包含了这个库。然后,解析请求并获取上传的文件: ```java import org.apache.struts.upload.FormFile; ...

    实现Struts上传多个文件

    - `commons-fileupload.jar`(用于文件上传) - `commons-logging.jar` - `commons-validator.jar` - `jakarta-oro.jar` - `struts.jar` 这些库可以确保Struts框架正常运行并支持文件上传功能。 #### 2. 配置...

    struts 单文件上传和多文件上传带进度条

    Struts2提供了一个`FileUpload`拦截器,用于处理文件上传请求。在Action类中,我们需要定义一个或多个`java.io.File`或`org.apache.struts2.dispatcher.multipart.FileItem`类型的属性,这些属性会被用来存储上传的...

    Struts1.2 formfile实现批量上传

    在Struts1.2框架中,FormFile是用于处理文件上传的一个关键组件,尤其是在实现批量上传功能时。本文将深入探讨如何使用FormFile在Struts1.2中实现实现批量文件上传,以及相关的技术要点。 一、Struts1.2简介 Struts...

    struts 操作文件上传下载

    在Struts 1.x中,文件上传主要依赖于`commons-fileupload`和`commons-io`两个库。以下是一些关键知识点: 1. **表单设置**:在HTML表单中,必须包含`enctype="multipart/form-data"`属性,以便发送二进制数据。 2....

    struts文件上传

    在Struts中,文件上传主要依赖于`commons-fileupload`和`commons-io`这两个库。在struts-config.xml配置文件中,你需要声明一个处理文件上传的ActionForm,比如`UploadForm`,并在其中定义一个类型为`java.io.File`...

    struts 文件上传 Struts实现文件上传

    Struts 提供了强大的支持来处理文件上传操作。下面将详细介绍Struts 实现文件上传的相关知识点。 ### 1. Struts 框架概述 Struts 是Apache软件基金会下的一个开源项目,其主要目标是提供一个可扩展且易于维护的...

    struts 实现文件上传与下载

    ### Struts实现文件上传与下载的关键知识点 #### 一、Struts框架简介 Apache Struts是一个开源的MVC(Model-View-Controller)框架,用于简化Java Web应用程序的开发过程。Struts提供了一系列的工具和服务来帮助...

    jspsmart + struts1.2 以及用 formfile 上传例子

    myeclipse 工程 上传页:index.html 有两个例子: 一个是 jspsmart + strust 1.2 另一个用的 struts 自带的 formfile 环境:winxp jdk1.4.2 tomcat5.0或weblogic8.1 发布前,最好先编译一下,毕竟JDK版本太老了

    基于struts的文件上传下载源代码

    你需要添加一个类型为`java.io.File`或`org.apache.struts.upload.FormFile`的属性来处理上传的文件。 3. `JSP页面`:用于创建HTML表单,包括文件输入字段`&lt;input type="file"&gt;`。用户通过这个表单选择要上传的文件...

    基于Struts文件上传(FormFile)详解

    本文将详细介绍基于Struts文件上传的实现方法,包括在JSP文件中定义文件上传表单、在Form表单中定义FormFile、利用Struts文件进行关联Form、获取上传文件的各种信息等。 在JSP文件中定义文件上传表单 在Struts中,...

    struts2文件上传下载

    在JSP页面(如`index.jsp`)中,需要包含Struts2的标签库,并使用`s:file`标签来提供一个文件上传控件。同时,`&lt;s:form&gt;`标签的`action`属性指定了上传处理的Action方法。用户上传文件后,表单提交至`/fileManage/...

    Struts1.x的上传文件示例

    在服务器端,你需要使用Apache的Commons FileUpload库来解析上传的文件。首先,确保在项目中引入了这个库。然后,在Action的execute方法中,使用`ServletFileUpload`和`FileItemIterator`来迭代并处理每个上传的文件...

    struts1上传多个文件同时

    3. **使用第三方库**:Apache Commons FileUpload是Java中处理文件上传的常见库,它可以很好地与Struts1集成。你需要在服务器端解析请求,使用Commons FileUpload处理上传的多个文件。 4. **编写Action**:在Action...

Global site tag (gtag.js) - Google Analytics