您的位置:首页 > 编程语言 > Java开发

Struts的FormFile与Commons-FileUp…

2013-09-11 16:06 134 查看
来自:http://www.iteye.com/topic/212566


前一段时间刚来公司,看到一个项目中以前有人写的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。

看这几行:

Java代码




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来看看:

Java代码




public static final long DEFAULT_SIZE_MAX = 250 * 1024 * 1024;



public static final int DEFAULT_SIZE_THRESHOLD = 256 * 1024;

还有,最最重要的实现方法:

Java代码




public void handleRequest(HttpServletRequest request) throws ServletException

再看看这个函数内部是怎么实现的吧?

Java代码


// 使用了DiskFileUpload。

// (Commons-FileUpload很老版本的一个上传实现类了,还在用?我的显示是Deprecated)

DiskFileUpload upload = new DiskFileUpload();

// 上传最大值

upload.setSizeMax((int) getSizeMax(ac));

// 上传文件在内存中使用的缓冲区大小

upload.setSizeThreshold((int) getSizeThreshold(ac));

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

pload.setRepositoryPath(getRepositoryPath(ac));

接着看handleRequest如何处理request的:

Java代码


// 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控件看看上传过程中可能抛出多少异常?

Java代码




//所有上传异常的父类

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函数:

Java代码


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文件中写配置:

Xml代码






<</span>controller>







<</span>set-property property="multipartClass"

value="com.amplesky.commonmodule.struts.AmpleskyMultipartRequestHandler" />



<</span>set-property property="maxFileSize" value="15M" />



<</span>set-property property="memFileSize" value="5M" />

</</span>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
都交给了上层去处理了,根本不会抛出来。所以这两种异常是抓不到的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐