org.apache.struts.upload

这是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));

   // 存在硬盘的什么地方,一般是默认

   upload.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里获取异常就可以判断上传过程中出了什么问题了,而且在上传过程中文件一旦超出了规定大小,或者磁盘大小不足的情况会立即中断上传的。这样我们的功能就实现了。

   最后,感慨一下,

感觉commons-fileupload还是挺好用的,但是FormFile的使用不大好,基本上误导能力很强,网上关于FormFile的资料说明很少,以上这些都是我自己摸索出来的。
Struts 1都到了1.3版本了,但是对于FormFile的实现依然使用commons-fileupload-1.0版本的DiskFileUpload类,可见更新也不怎么样。其实我觉得这是apache大部分开源工具的一个通病:更新确实不怎么快,commons框架里边的源代码和jar包不一致,还有很多是2004或者2003年的…要命的是居然不支持javase5的新特性????
认为把异常放到request的attributs里不好,曾经尝试过抛出异常然后通过配置exception-controller来抓去异常处理,但是抓不到,怎么都抓不到,后来翻了大半天源码才发现:struts在处理异常请求的时候将出现的ServletException和IOExcepton都交给了上层去处理了,根本不会抛出来。所以这两种异常是抓不到的。

猜你喜欢

转载自xianlincai.iteye.com/blog/2334451