博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅谈对Struts2上传文件的理解
阅读量:4963 次
发布时间:2019-06-12

本文共 5942 字,大约阅读时间需要 19 分钟。

孙鑫的书《Struts2深入详解》509页是关于限制Struts2上传文件的最大长度的内容。其中谈到fileUpload拦截器只是当文件上传到服务器上之后,才进行的文件类型和大小判断。Struts2框架底层默认用的是apache的commons-fileupload组件对上传文件进行接受处理。

 

  通过struts.multipart.maxSize属性来对文件大小进行限定时,将直接影响到commons-fileupload组件的文件大小设定,默认是2M。当上传文件超过了这个尺寸时,将从commons-fileupload组件中抛出SizeLimitExceededException异常。Struts2上传文件拦截器捕获到这个异常后,将直接把该异常信息设置为Action级别的错误信息。

  经过我的测试和对源代码的Debug,发现确实如孙鑫书中所言,如果上传文件大于2M时,在页面上就出现了一堆英文的错误信息,大致是:therequestwas rejected because its size....exceeds theconfiguredmaximum...并且在fieUpload中将来自MultiPartRequestWrapper型request对象的错误信息给加到了Action的错误中。

  这时候,你在ApplicationResources.properties中自定义的上传文件过大的错误信息根本不起作用。原因就如书上所言,在底层commons-fileupload组件中就把异常给抛出来了文件根本没被上传,所以到了fileUpload拦截器时,根据取不到文件,当然也就没法对文件的类型和大小进行判断了。

  然而,这个异常直接带来两个问题:

  1、在页面上显示了英文的错误信息。这样的信息显然不是我们想要的。

  2、由于错误的产生,原来页面上输入的其他文本内容也都不见了,也就是说ms注入失败。

  带着这两个问题,我们来探寻一下Struts2对于请求的处理过程。

  注:这并不是一篇关于Struts2请求过程的介绍,主要是为了解决以上两个问题,才引起的简单分析。

  首先当然我们要拿FilterDispatcher开刀。

  在doFilter方法中调用了prepareDispatcherAndWrapRequest方法,为了包装出Struts2自己的request对象,在prepareDispatcherAndWrapRequest方法中调用Dispatcher类的wrapRequest方法,在这个方法里,会根据请求内容的类型(提交的是文本的,还是multipart/form-data格式),决定是使用tomcat的HttpServletRequestWrapper类分离出请求中的数据,还是使用Struts2的MultiPartRequestWrapper来分离请求中的数据。

  注:向服务器请求时,数据是以流的形式向服务器提交,内容是一些有规则东东,我们平时在jsp中用request内置对象取meter时,实际上是由tomcat的HttpServletRequestWrapper类分解好了的,无需我们再分解这些东西了。

  当然,在这里,我们研究的是上传文件的情况,所以,由于form中设定的提交内容是媒体格式的,所以,Dispatcher类的wrapRequest方法会将请求交由MultiPartRequestWrapper类来处理。

  MultiPartRequestWrapper这个类是Struts2的类,并且继承了tomcat的HttpServletRequestWrapper类,也是我们将用来代替HttpServletRequest这个类的类,看名字也知道,是对多媒体请求的包装类。

  Struts2本身当然不会再造个轮子,来解析请求,而是交由Apache的commons-fileupload组件来解析了。

  在MultiPartRequestWrapper的构造方法中,会调用MultiPartRequest(默认为JakartaMultiPartRequest类)的parse方法来解析请求。

  在Struts2的JakartaMultiPartRequest类的parse方法中才会真正来调用commons-fileupload组件的ServletFileUpload类对请求进行解析,至此,Struts2已经实现了将请求转交commons-fileupload组件对请求解析的全过程。剩下的就是等commons-fileupload组件对请求解析完毕后,拿到分解后的数据,根据field名,依次将分解后的field名和值放到ms(HashMap类型)里,同时JakartaMultiPartRequest类重置了HttpServletRequest的好多方法,比如熟知的getmeter、getmeterNames、getmeterValues,实际上都是从解析后得到的那个ms对象里拿数据,在这个过程,commons-fileupload组件也乖乖的把上传的文件分析好了,JakartaMultiPartRequest也毫不客气的把分解后的文件一个一个的放到了files(HashMap类型)中,实际上此时,commons-fileupload组件已经所有要上传的文件上传完了。

  至此,Struts2实现了对HttpServletRequest类的包装,当回到MultiPartRequestWrapper类后,再取一下上述解析过程中发生的错误,然后把错误加到了自己的errors列表中了。同样我们会发现在MultiPartRequestWrapper类中,也把HttpServletRequest类的好多方法重载了,毕竟是个包装类嘛,实际上对于上传文件的请求,在Struts2后期的处理中用到的request都是MultiPartRequestWrapper类对象,比如我们调用getmeter时,直接调用的是MultiPartRequestWrapper的getmeter方法,间接调的是JakartaMultiPartRequest类对象的getmeter方法。

  注:从这里,我们就可以看出,JakartaMultiPartRequest是完全设计成可以替换的类了。

  然后继续向回返,到了Dispatcher类的wrapRequest方法,直接把MultiPartRequestWrapper对象返回了,我们就终于回到了FilterDispatcher类的prepareDispatcherAndWrapRequest方法,此时,我们拿到了完全解析好了的request对象(MultiPartRequestWrapper类),该对象又进一步被返回到了FilterDispatcher类的doFilter方法,也就是回到了出发点,至此,doFilter中拿到的request对象就是一个将请求中的数据分解好的了HttpServletRequest对象,我们完全可以用getmeter方法取其中的数据了,同时,我们也可以用getFiles得到文件数组了。

  doFilter方法中,会进一步调用actionMapper的getMapping方法对url进行解析,找出命名空间和action名等,以备后面根据配置文件调用相应的拦截器和action使用。

  关于doFilter方法中下一步对Dispatcher类的serviceAction方法的调用,不再描述,总之在action被调用之前,会首先走到fileUpload拦截器(对应的是FileUploadInterceptor类),在这个拦截器中,会先看一下request是不是MultiPartRequestWrapper,如果不是,就说明不是上传文件用的request,fildUpload拦截器会直接将控制权交给下一个拦截器;如果是,就会把request对象强转为MultiPartRequestWrapper对象,然后调用hasErrors方法,看看有没有上传时候产生的错误,有的话,就直接加到了Action的错误(Action级别的)中了。

  另外,在fileUpload拦截器中会将MultiPartRequestWrapper对象中放置的文件全取出来,把文件、文件名、文件类型取出来,放到request的meters中,这样到了ms拦截器时,就可以轻松的将这些内容注入到Action中了,这也就是为什么fileUpload拦截器需要放在ms拦截器前面的理由。在文件都放到request的meters对象里之后,fileUpload拦截器会继续调用其他拦截器直到Action等执行完毕,他还要做一个扫尾的工作:把临时文件夹中的文件删除(这些文件是由commons-fileupload组件上传的,供你在自己的Action中将文件copy到指定的目录下,当action执行完了后,这些临时文件当然就没用了)。

  你好,你还在看吗?呵呵,是不是太多了,也太乱了,没办法,Struts2就是这样的调用的。也不知道Struts2有没有公开其Sequence图,我是想画一个,不过,太懒,还是看着代码说说吧。

  如果上面看烦了,也完全可以不看了,直接看下面的。

  在上面一番分析之后,文件上传的全过程就结束了。

  我们回到我们的问题上来。

  先看第一个:

  1、在页面上显示了英文的错误信息。这显然不是我们想要的。

  没办法了,commons-fileupload组件没想到国际化,在FileUploadInterceptor拦载器中,也没想着国际化,直接放到Action的错误中了,就没他事了,三种做法:

  (1)在错误显示之前,把这条错误给换掉,应该难度不大,我没做留给你做了。

  (2)或者重写一下JakartaMultiPartRequest这个类,把捕捉到的异常信息换成自己的,然后,通过Struts2的配置文件,把我们重写的这个parser换上去用。

  (3)直接改commons-fileupload组件的类,换成中文的。

  我具体说一下第(3)种做法:找到FileUploadBase类,把902行~908行改一下。

  FileUploadException ex =

  new SizeLimitExceededException(

  "the request was rejected because"

  + " its size (" + pCount

  + ") exceeds the configured maximum"

  + " (" + pSizeMax + ")",

  pCount, pSizeMax);

  =>

  FileUploadException ex = new SizeLimitExceededException(

  "服务器拒绝了您的请求,原因可能是向服务器提交的数据发生了丢失。", pCount, pSizeMax);

  把914行~918行改一下。

  throw new SizeLimitExceededException(

  "the request was rejected because its size ("

  + requestSize

  + ") exceeds the configured maximum ("

  + sizeMax + ")",

  =>

  thrownewSizeLimitExceededException("服务器拒绝了您的请求,原因是提交数据量过大(通常是由于上传文件过大),请返回上页重试。"

  + " (最大字节数:" + sizeMax / 1024

  + "K)", requestSize, sizeMax);

  再看一下第二个问题。

  2、由于错误的产生,原来页面上输入的内容也全部不见了,也就是说ms注入失败。

  关于这个问题我在javaeye上搜索到一篇文章(使用的commons-fileupload组件的jar包似乎比较老)。

  http://www.javaeye.com/topic/197345

  虽然按照此文,当上传失败时,能够将其他输入内容显示出来,但是这样做的结果是全部的文件肯定会上传到服务器上,也就是说,虽然是页面上报了文件因为太大,请求被拒绝的错,但是文件依然会被上传到服务器上,commons-fileupload组件根本没会去拦文件的上传。

  在这里要说明一下,如果你不抛出这个异常,请求的流会继续向服务器上传,只有当整个流上传完了之后,commons-fileupload组件才能正确的分析出文件部分、文本部分。所以,在这里抛出异常是不得已的作法,如果不抛异常,后果是虽然页面报错,但文件还是会被传到服务器的上,这一步根本没挡住输入流的上传,如果没挡住的话,大家想想会有什么后果?

  所以,综上所述,对于第二个问题,如果出现了这个异常,我们根本无法让原来输入的内容还显示出来的,因为commons-fileupload组件并没有解析全部的输入内容,直接给出异常了,到了ms拦截器中,request里就是空的,根本取不到meter,所以也就无法注入到Action中了。这种情况下,只能显示一个告知用户由于提交数据量过大,服务器拒绝了请求的错误信息,比较好的方法是,直接跳到一个专门的页面,提示用户,然后让用户点返回来再次输入,否则用户会感觉上传文件大就大吧,怎么连我输入的其他一些内容也没给保存住。当然,如果能用Ajax来上传文件,对客户的操作体验可能是最好的,但是,这样可能会导致服务器上有些挂空的文件(上传后从来没被用过),需要想法清除的。

  整个分析下来,我们说第二个问题基本上是无法避免的。

  编辑推荐

  在Eclipse中开发struts应用程序

  手把手教你在Eclipse中配置开发Struts

  Eclipse下开发struts完整解决乱码问题

  Struts相关背景介绍

  使用Easy Struts for Eclipse开发Struts

转载于:https://www.cnblogs.com/yjhrem/articles/2242177.html

你可能感兴趣的文章
【CodeM初赛B轮】A 贪心
查看>>
maven 手动构建项目
查看>>
JSON遇到的问题
查看>>
float浮动深入理解
查看>>
流程控制之 if 判断
查看>>
OpenCV图像处理篇之边缘检测算子
查看>>
hdu1331Function Run Fun
查看>>
web容器启动后自动执行程序的几种方式比较
查看>>
expdp/impdp 参数说明,中英对照
查看>>
软件工程—团队作业1
查看>>
centos7 关闭默认firewalld,开启iptables
查看>>
UVALive 3989Ladies' Choice(稳定婚姻问题)
查看>>
来一发!!
查看>>
POJ1679The Unique MST(次小生成树)
查看>>
焦作网络赛K-Transport Ship【dp】
查看>>
面试题 —— 面向对象
查看>>
英文语法 —— 句型(定语从句)
查看>>
markdownpad 2 的使用
查看>>
数据结构与算法的实现 —— 结点定义与数据结构的选择
查看>>
N 个互异数的数组的平均逆序数
查看>>