struts1.2 文件上传处理

前一段时间刚来<nobr style="border-bottom:#6600ff 1px dotted; background-color:transparent; color:#6600ff; text-decoration:underline">公司</nobr>,看到一个项目中以前有人写的struts代码。是使用了FormFile来处理关于文件上传的模块。但是用力一段时间后,发现出问题了。写完的这个模块<wbr>,上传文件是没有问题的,但是当服务器的空间较小的时候<wbr>,穿一个比较大的文件就出问题了,文件还没有上传完<wbr>,就抛出一个错误的页面</wbr></wbr></wbr>,报告上传模块出了问题,而且是Tomcat默认的出错页面。

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

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

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

因此,原先的设计:在Action中判断文件大小是根本不能在上传过程中起到提示作用的<wbr>,因为这时候文件已经上传完了。而且这个设计还有一个确定就是不能<wbr>捕获上传过程中出现的任何问题。也就是说:在</wbr></wbr>Action里<nobr style="border-bottom:#6600ff 1px dotted; background-color:transparent; color:#6600ff; text-decoration:underline">我们</nobr>得到的FormFile对象是上传的一个结果,而不是一个未上传好就可以使用的对象!

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

这是struts1.1org.apache.struts.upload包的描述:

从上图我们可以看出有有CommonsMultipartRequestHandlerDiskMultipartRequestHandler两个类实现了MultipartRequestHandler接口。

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

但是这里有两个类,CommonsMultipartRequestHandlerDiskMultipartRequestHandler。到底哪个是处理FormFile的上传的呢?这个问题应该从org.apache.struts.config包里来找。

org.apache.struts.config包是用来处理struts配置文件的数据的包。找到org.apache.struts.config. ControllerConfig

看这几行:



/**

*ThefullyqualifiedJavaclassnameoftheMultipartRequestHandler

*classtobeused.

*/

protectedStringmultipartClass=

"org.apache.struts.upload.CommonsMultipartRequestHandler";


publicStringgetMultipartClass(){

return(this.multipartClass);

}
publicvoidsetMultipartClass(StringmultipartClass){

if(configured){

thrownewIllegalStateException("Configurationisfrozen");

}

this.multipartClass=multipartClass;

}


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

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

马上转到org.apache.struts.upload<wbr>.CommonsMultipartRequestHandler</wbr>来看看:



/**

*默认文件上传的大小是250M

*/

publicstaticfinallongDEFAULT_SIZE_MAX=250*1024*1024;

/**

*上传文件在内存中使用的缓冲区大小,超过次数值的数据写入硬盘。

*/

publicstaticfinalintDEFAULT_SIZE_THRESHOLD=256*1024;

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

/**

*Parsestheinputstreamandpartitionstheparseditemsintoasetof

*formfieldsandasetoffileitems.Intheprocess,theparseditems

*aretranslatedfromCommonsFileUpload<code>FileItem</code>instances

*toStruts<code>FormFile</code>instances.

*

*
@paramrequestThemultipartrequesttobeprocessed.

*

*
@throwsServletExceptionifanunrecoverableerroroccurs.

就是这个函数处理上传文件的request,把request交给

CommonsFileUpload控件处理,并解析FileItem转换成Struts的FormFile。

*/

publicvoidhandleRequest(HttpServletRequestrequest)

throwsServletException

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

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

DiskFileUploadupload
=newDiskFileUpload();

//上传最大值

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

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

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

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

upload.setRepositoryPath(getRepositoryPath(ac));


接着看handleRequest如何处理request的:



//Parsetherequestintofileitems.

Listitems
=null;

try{

items
=upload.parseRequest(request);

}
//这里是关键:上传过程中出了超出最大值的异常了,如何处理?

catch(DiskFileUpload.SizeLimitExceededExceptione){

//Specialhandlingforuploadsthataretoobig.

request.setAttribute(

MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED,

Boolean.TRUE);

return;

}

//出了其他异常,如enctype不对,磁盘空间不足怎么办?

catch(FileUploadExceptione){

log.error(
"Failedtoparsemultipartrequest",e);

thrownewServletException(e);

}

这次一目了然了:

Struts根本没有把上传过程中出的超出最大值的异常带到Action,因为那是不可能的,而是把它放到了rquestAttribute

而出了其他异常如enctype不对,磁盘空间不足怎么办?很遗憾,Struts没有去处理它,而是log了一下,抛给了上一层了。

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

在此之前,我们得先去最新版的commons-fileupload控件看看上传过程中可能抛出多少异常?

//所有上传异常的父类

org.apache.commons.fileupload<wbr>.FileUploadException</wbr>

//注意:这个类的类名是FileUploadBase.SizeLimitExceede<wbr>dException</wbr>是个public//部类。上传的formdata总的数据超出了规定大小

org.apache.commons.fileupload<wbr>.FileUploadBase.SizeLimitExceed<wbr>edException</wbr></wbr>

//注意:也是个内部类。这个才是上传的文件超出了规定大小

org.apache.commons.fileupload<wbr>.FileUploadBase.FileSizeLimitEx<wbr>ceededException</wbr></wbr>

其它的,也看看吧:

org.apache.commons.fileupload<wbr>.FileUploadBase.FileUploadIOExc<wbr>eption</wbr></wbr>

org.apache.commons.fileupload<wbr>.FileUploadBase.InvalidContentT<wbr>ypeException</wbr></wbr>

org.apache.commons.fileupload<wbr>.FileUploadBase.IOFileUploadExc<wbr>eption</wbr></wbr>

org.apache.commons.fileupload<wbr>.FileUploadBase.UnknownSizeExce<wbr>ption</wbr></wbr>

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

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

这是我写的CommonsMultipartRequestHandler替代类的public void handleRequest(HttpServletReques<wbr>t request) throws ServletException</wbr>函数:



publicvoidhandleRequest(HttpServletRequestrequest)throwsServletException

...{


//Gettheappconfigforthecurrentrequest.

ModuleConfigac
=(ModuleConfig)request.getAttribute(Globals.MODULE_KEY);


//DiskFileItem工厂,主要用来设定上传文件的参数

DiskFileItemFactoryfileItemFactory
=newDiskFileItemFactory();

//上传文件所用到的缓冲区大小,超过此缓冲区的部分将被写入到磁盘

fileItemFactory.setSizeThreshold((
int)this.getSizeThreshold(ac));

//上传文件用到的临时文件存放位置

fileItemFactory.setRepository(
this.getRepository(ac));


//使用fileItemFactory为参数实例化一个ServletFileUpload对象

//注意:该对象为commons-fileupload-1.2新增的类.

//对于1.2以下的commons-fileupload版本并不存在此类.

ServletFileUploadupload
=newServletFileUpload(fileItemFactory);

//从session中读取对本次上传文件的最大值的限制

StringmaxUploadSize
=(String)request.getSession().getAttribute(BasicConstants.maxUploadSize);

//获取struts-config文件中controller标签的maxFileSize属性来确定默认上传的限制

//如果struts-config文件中controller标签的maxFileSize属性没设置则使用默认的上传限制250M.

longdefaultOrConfigedMaxUploadSize=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");


//Createthehashtablestobepopulated.

elementsText
=newHashtable();

elementsFile
=newHashtable();

elementsAll
=newHashtable();


//Parsetherequestintofileitems.

Listitems
=null;

//ServletFileUpload类来处理表单请求

//抛出的异常为FileUploadException的子异常

//如果捕获这些异常就将捕获的异常放到session中返回.

try

...{

items
=upload.parseRequest(request);

}


catch(FileUploadBase.SizeLimitExceededExceptione)

...{

//请求数据的size超出了规定的大小.

request.getSession().setAttribute(BasicConstants.baseSizeLimitExceededException,e);

return;

}


catch(FileUploadBase.FileSizeLimitExceededExceptione)

...{

//请求文件的size超出了规定的大小.

request.getSession().setAttribute(BasicConstants.baseFileSizeLimitExceededException,e);

return;

}


catch(FileUploadBase.IOFileUploadExceptione)

...{

//文件传输出现错误,例如磁盘空间不足等.

request.getSession().setAttribute(BasicConstants.baseIOFileUploadException,e);

return;

}


catch(FileUploadBase.InvalidContentTypeExceptione)

...{

//无效的请求类型,即请求类型enctype!="multipart/form-data"

request.getSession().setAttribute(BasicConstants.baseInvalidContentTypeException,e);

return;

}


catch(FileUploadExceptione)

...{

//如果都不是以上子异常,则抛出此总的异常,出现此异常原因无法说明.

request.getSession().setAttribute(BasicConstants.FileUploadException,e);

return;

}


//Partitiontheitemsintoformfieldsandfiles.

Iteratoriter
=items.iterator();

while(iter.hasNext())

...{

FileItemitem
=(FileItem)iter.next();


if(item.isFormField())

...{

addTextParameter(request,item);

}


else

...{

addFileParameter(item);

}


}


}




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

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

这样:在struts-config文件中写配置:



<!--==========ControllerConfiguration================================-->

<controller>

<!--The"input"parameteron"action"elementsisthenameofa

localorglobal"forward"ratherthanamodule-relativepath
-->

<set-propertyvalue="true"property="inputForward"/>

<set-propertyvalue="text/html;charset=UTF-8"

property
="contentType"/>

<!--通过写类的全名来替代struts默认的MultipartRequestHandler-->

<set-propertyproperty="multipartClass"value="com.amplesky.commonmodule.struts.AmpleskyMultipartRequestHandler"/>

<!--规定的上传文件的最大值-->

<set-propertyproperty="maxFileSize"value="15M"/>

<!--缓冲区大小-->

<set-propertyproperty="memFileSize"value="5M"/>

</controller>

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

最后,感慨一下,

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值