Struts FileUpload 异常处理之Processing of multipart/form-data request failed.
在使用FileUpload的过程中,有一个经常抛出异常如下:
ERROR [http-8081-Processor21] (CommonsMultipartRequestHandler.java:201) -2008-04-10 11:20:27,671 Failed to parse multipart request
org.apache.commons.fileupload.FileUploadException: Processing of multipart/form-data request failed. temp\upload__3d7cf8b_11936276cf8__7ffd_00000011.tmp (系统找不到指定的路径。)
at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:384)
at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:268)
at org.apache.struts.upload.CommonsMultipartRequestHandler.handleRequest(CommonsMultipartRequestHandler.java:193)
at org.apache.struts.util.RequestUtils.populate(RequestUtils.java:443)
at org.apache.struts.action.RequestProcessor.processPopulate(RequestProcessor.java:804)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:203)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
问题分析:
在我使用Struts FileUpload时有以下几个关键部分:
定义Form的Enctype属性multipart/form-data,添加<input type=file>的域; 在FormBean中定义与file域名称相同、类型为FormFile的属性; 在strut-config.xml定义或修改controller标记,分别设置maxFileSize=10M、bufferSize=4096、tempDir=temp、inputForward=true等属性; 获得FormFile类型的属性,将字节流写入指定的文件中。
而在不超出maxFileSize的范围中有一些较大文件都不能上传成功,并抛出上述异常,在指定的tempDir目录中也不能找到相应的tmp文件。在已知的单独使用commons-fileupload时经常使用的handler是ServletFileUpload或DiskFileUpload,而在查到的资料中的解释是使用DiskFileUpload就可以解决上面的问题,那么StrutsFileUpload是怎么使用这些类来处理上传的呢?查看struts-config_1_2.dtd有一个属性是multipartClass是用来设置处理这种请求的类,默认值是CommonsMultipartRequestHandler,那么应该有其它不同的处理类,于是在"org.apache.struts.upload中找到类DiskMultipartRequestHandler。这个类的处理方式是直接在临时文件目录写临时文件。那么定义multipartClass=org.apache.struts.upload.DiskMultipartRequestHandler,则异常不再出现。
但是javadoc中明明白白的写的是在commons-fileupload以后的版本中DiskMultipartRequestHandler类将由CommonsMultipartRequestHandler替代,使用CommonsMultipartRequestHandler才是明智的选择。那么分析一下,发现后者可用的原因就应该在于后者是使用硬盘空间作为交换空间,则前者就应当是使用内存做为交换空间的,那么使用内存空间应该是有边界限制的,这个属性就是memFileSize,其默认值是256K,在上传抛出异常时使用的文件就是大于这个数字的文件,发现它对应的其实就是DiskFileItemFactory的sizeThreshold属性。而对memFileSize的解释就是上传时可以在内存中保留的文件的最大值,超出这个数字则保存在其它的介质中如硬盘。也就是说要正常使用CommonsMultipartRequestHandler类处理上传请求,可以将memFileSize定义到与maxFileSize一样大,但显而易见的是一旦有更大的上传文件需求时,就会重新遇到问题,如此大的内存开销是显然是不合理的。
按道理来讲,CommonsMultipartRequestHandler做为multipartClass默认值和下一版本的保留类,应该是可以正确的使用配置文件中定义的memFileSize属性的,那么既然在异常中有“系统找不到指定的路径”字样,那就从文件系统的路径来试着解决问题。tempDir指定的目录是temp,异常的提示应该就是找不到这个目录,从本机应用中来看这个temp就是tomcat根目录下的temp,但就是这个目录在我本机上是可以找到的,在服务器上却总是提示异常。查看struts-config_1_2.dtd,对于这个参数的解释是“指定文件上传时的临时工作目录.如果没有设置,将才用Servlet容器为web应用分配的临时工作目录”。那么在我的应用中如果不指定tempDir,这个目录就是%TOMCAT_HOME%\temp;如果指定的话,就要指定绝对路径,如“C:\\temp”。
解决方法:
1、指定multipartClass为org.apache.struts.upload.DiskMultipartRequestHandler,不推荐使用这种方式;
2、指定memFileSize与maxFileSize相同的值,用于上传文件较小的情况,这种方式其实也是一种错误的方式,不推荐;
3、指定tempDir为绝对目录;
4、如果应用服务器可以为应用分配临时目录的话,可以不指定tempDir;
5、即便tempDir指定的是相对目录,如tempDir="temp",也并不是错误的,它应该是存在于当然应用的一个相对目录。
补充:
tempDir指定的目录中可能会随着时间推移出现很多后缀为"tmp"的垃圾文件,commons-fileupload提供了一个不错的解决方法,就是把下面的代码加入到web.xml中即可。
<listener>
<listener-class>
org.apache.commons.fileupload.servlet.FileCleanerCleanup
</listener-class>
</listener>
需要注意的是FileCleanerCleanup是commons-fileupload1.2以后的版本才有的类。