手工处理 Struts2 框架上传的文件

在使用了 Struts2 框架的系统中,对于处理像下面这种表单上传文件时:

自然而然的想法就是在 Action 中声明变量 File upload 和 String desc,请求提交到这个 Action 后,在 execute() 方法中就能直接使用 upload 和 desc 了,它们已被 Struts2 框架(org.apache.struts2.interceptor.FileUploadInterceptor 监听器) 赋上了相应的值了。

因为维护的是一个古老的项目,请求都是直接提交给 jsp。在这个项目中套上了 Struts2 已是不易了。原来项目是用的 jspSmartUpload 来处理上传文件的,Struts2 一上 jspSmartUpload 便不能正常工作了,因为 Struts2 的过滤器 org.apache.struts2.dispatcher.FilterDispatcher 拦截的是所有的请求,在交把请求交给 jspSmartUpload 之前请求 request 就已被处理过了,即使是把 struts2-core-2.x.x.jar 中的 struts-default.xml fileUpload 取消了也是如此。

暂时又不想再新加一个 Action,声明 upload:File 和 desc:String 直接接收参数,这样改动的话实在是大,现在 struts.xmlstruts.properties 文件还是空的呢。所以姑且在原来那个 jsp 中处理吧。

最早做过 jsp 文件上传的人都知道,给 form 加上 enctype="multipart/form-data" 属性后,request.getParameter("desc") 取输入框的值就失灵了,因为页面请求数据是以流的形式发送给服务器的,所以 jspSmartUpload 用了它自己的 Request, com.jspsmart.upload.SmartUpload.getRequest().getParameter("desc") 来接收文本框数据,但对于 Struts2 处理过的 request jspSmartUpload 就无能为力了。

那么在 Struts2 中的 jsp 如何获取到 enctype="multipart/form-data" 表单传递过来的文本输入和文件呢?


·获取文本框的值
,仍然可用 request.getParameter("desc"),因为此时的 request 是由 org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper 实现的。

对比一下不同时候 request 的具体实现类(Tomcat 环境中)

form enctype multipart/form-data application/x-www-form-urlencoded
Struts2 org.apache.struts2.dispatcher.multipart
.MultiPartRequestWrapper
org.apache.struts2.dispatcher
.StrutsRequestWrapper
Struts1 org.apache.struts.upload
.MultipartRequestWrapper
org.apache.coyote.tomcat5
.CoyoteRequestFacade
无框架 org.apache.coyote.tomcat5
.CoyoteRequestFacade
org.apache.coyote.tomcat5
.CoyoteRequestFacade


·获取上传来的文件
用就要对 request 作个转型,才能调用到相应的方法

其他方法可以查看 MultiPartRequestWrapper API,MultiPartRequestWrapper 是继承自 org.apache.struts2.dispatcher.StrutsRequestWrapper 的。

最后,用了 Struts2 来上传文件,最好在 web.xml 中加上 ActionContextCleanUp 过滤器以避免一些未不预知的异常。

网上有人说是要加 ActionContextCleanUp 过滤器的,ActionContextCleanUp 的代码注释是它易于同 SiteMesh 的整合,至于为何与文件上传扯上关系,我以后也会关注的。

对了还要在项目中引入 commons-fileupload-x.x.x.jar 和 commons-io-x.x.jar 包,其他没有什么特别的配置,默认即可。相信本文的实用性不强,不会有人用 jsp 来处理这些事情,参考价值可能还有一些。

本文链接 https://yanbin.blog/manual-struts2-file-upload/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

18 Comments
Inline Feedbacks
View all comments
GoKu
GoKu
16 years ago

楼主的文章都是精品

网上买书
16 years ago

真的是精品

mzm
mzm
16 years ago

能有更具体的吗,我这几天都在研究这个,怎么将SmartUpload整合在ssh中,谢谢楼主!

隔叶黄莺
16 years ago

你用了 Struts2 的话,就没必要,也很难用上 JspSmartUpload,因为 Struts2 比以往的 Struts1 不一样的,不再用 ActionServlet 来映射 *.do 的请求,而是用一个 FilterDispatcher 来拦截所有的 /* 的请求,所以无论你用什么形式的网址访问,你的 multipart/form-data 的表单提交的数据都会被 Struts2 处理加工一番,而 JspSmartUpload 想要的是最原始的 Request,因此给 Struts2 嚼过一次的 Request,再让 JspSmartUpload 再 initialize(pageContext) 时,其时 Request 中的数据已不是它所期望的结构了,所以会全乱套的。

所以既然用了 Struts2 就用它提供的文件上传功能吧。

但如果你还在用 Struts1 的话,还有些机会用上 SmartUpload,因为 Struts1 处理的是 *.do 请求,所以只要你的上传页面不要提交给 *.do 的 URL 进行处理就可以像你以前那般使用 SmartUpload 了。

最后仍然不建议用 JspSmartUpload 了,Struts1 的文件上传功能也很不错的,Struts2 对于上传文可是提供了三种组件的选择。在 Struts1 中你也可以手工来处理文件的上传。何况 Struts2 在拦截所有请求的同时还给你加了一个字符集过滤器,这对我们开发中文件的系统是个福音,不用自己加个 CharacterEncodingFilter 了,可以解决上传时文件名的乱码问题。

这也是 Struts2 拦截所有请求而不只是 *.do 的原因,同时也是为什么 Struts2 选择了 Filter 而不是像 Struts1 的 ActionServlet 来映射 URL 的缘故,因为 Servlet 做不到,Filter 才能组成成 Filter 链。

mzm
mzm
16 years ago

谢谢楼主,以前的系统是使用jsp+javabean实现的,现在是整合到struts2.0+hibernate3.2+spring2.5,改成struts2.0改动比较大,需要修改页面比较多,还有没有其他的方法;上传文件的代码都写在jsp中,如改成struts2.0需要跳转action,有例子没,谢谢楼主。

隔叶黄莺
16 years ago

我改了一个 jsp 的项目,用上了 Struts2 ,先不要大动干戈,并非一用上 Struts2 就得跳到 action 去。反正 Struts2 都是拦截所有请求,沿用原来一些东西,仍然可以暂时在 jsp 里改动。

至于 spring 和 hibernate 的加入,不用引响到 web 的架构,如果不觉得难看的话,在哪里 getBean() 或取到 getHibernateTemplate() 都可以先忍奈。

mzm
mzm
16 years ago

是说可以在jsp中定义所需要上传文件的基本信息,直接写方法。有相关例子没,就是实现文件上传,谢谢楼主。

隔叶黄莺
16 years ago

就是

MultiPartRequestWrapper mpRequest = (MultiPartRequestWrapper)request;

File[] files = mpRequest.getFiles("upload"); //文件现在还在临时目录中

String[] fileNames = mpRequest.getFileNames("upload");

request.getParameter("desc");

加了 Struts2 的 Filter 不用作任何处理了。

mzm
mzm
16 years ago

<%

MultiPartRequestWrapper mpRequest = (MultiPartRequestWrapper)request;

//File[] file = mpRequest.getFiles("upload"); //文件现在还在临时目录中

String[] fileNames = mpRequest.getFileNames("upload");

System.out.println(mpRequest.getFiles("upload").length);

int k=0;

for(int i=0;i<mpRequest.getFiles("upload").length;i++){

System.out.println(mpRequest.getFiles("upload")[i]);

try{

FileInputStream fis = new FileInputStream(mpRequest.getFiles("upload")[i]);

FileOutputStream fos = new FileOutputStream("E:/apache-tomcat-5.5.25/apache-tomcat-5.5.25/webapps/aiqcs");

byte[] buf = new byte[1024];

int j = 0;

while ((j = fis.read(buf)) != -1) {

fos.write(buf, 0, j);

k++;

}

fis.close();

fos.close();

}

catch(Exception e){

System.out.println(e);

}

}

%>

File[] file = mpRequest.getFiles("upload"); //文件现在还在临时目录中

在这句话报Type mismatch:cannot convert from File[] to File[]

System.out.println(mpRequest.getFiles("upload")[i]);打印的是临时地址:E:\apache-tomcat-5.5.25\apache-tomcat-5.5.25\work\Catalina\localhost\aiqcs\upload__4422d98_11a2e68a292__8000_00000102.tmp

FileOutputStream fos 报:java.io.FileNotFoundException: E:\apache-tomcat-5.5.25\apache-tomcat-5.5.25\webapps\aiqcs (拒绝访问。)

mail����.txt;出现乱码问题

mzm
mzm
16 years ago

现在是中文乱码问题,struts2.0的filter不是可以实现,在好好看看,谢谢楼主!

隔叶黄莺
16 years ago

struts.properties 或 struts.xml 中设置常量

struts.i18n.encoding=GBK

指定你要的字符集,默认是 UTF-8

mzm
mzm
16 years ago

现在有个字符编码冲突问题,在用ssh实现的模块中使用的字符格式是utf-8,老系统整合在ssh框架下,实现的模块功能也整合在老系统下,老系统使用的是字符格式是GB2312,有没有办法解决这个冲突,谢谢楼主。

隔叶黄莺
16 years ago

要不特殊处理,要不就是你现在的框架也用 GBK,其实用 UTF-8 只是一种愿望,如果不会有实际的需求去支持更多字符就 GBK 足矣,UTF-8 要比 GBK 多些存存储空间,也加大的网络传输。

Vance
Vance
16 years ago

楼主有无试过用COS在Struts2中成功实现上传啊?我试了好多方法都不行

有机会给我邮件,聊下~~

vinscheng@163.com

guosk
guosk
15 years ago

楼主的文章确实是精品啊!

不过有这样一个问题,原来一个系统是struts1开发的,整合了struts2后,原来struts1开发的文件上传功能(未用smartupload组件)用不了了!难道是struts1和struts2的兼容问题?或者是楼主所说的request被struts2处理过了,struts1用不了了?整合之后,原来struts1的其他功能都可以用。

guosk
guosk
15 years ago

我的邮件是290582054@qq.com。

guosk
guosk
15 years ago

再一次感谢楼主!

隔叶黄莺
15 years ago

@guosk

是的,Struts2 的 org.apache.struts2.dispatcher.FilterDispatcher 拦截了所有的请求,所以如果你还想用 struts1 的文件上传功能,那么可以在配置文件中把 Struts2 的 org.apache.struts2.interceptor.FileUploadInterceptor 拦截器关掉。