先看效果:
在struts2中上传是很简单的,struts2会先把文件写到临时文件中,以后在提供这个文件的File对象到action中。具体原理看这里:
http://blog.csdn.net/tom_221x/archive/2009/01/12/3761390.aspx。
利用servlet和common-upload.jar很容易实现显示文件上传的进度,在common-upload组件中实现一个ProgressListener接口,组件会把上传的实时进度传给你。但是想在struts2中,实时的显示进度是有些困难的。因为struts2把request对象给封装了,在Action中拿到request对象,如果是上传文件,那么struts2已经把文件写到文件系统里去了。
struts2上传文件的时候封装request对象其实是org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper,也就是说在action中拿到的实际类型是MultiPartRequestWrapper。片段如下:
view plaincopy to clipboardprint?
01.public class MultiPartRequestWrapper extends StrutsRequestWrapper {
02. protected static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestWrapper.class);
03. Collection<String> errors;
04. MultiPartRequest multi;
05. /**
06. * Process file downloads and log any errors.
07. *
08. * @param request Our HttpServletRequest object
09. * @param saveDir Target directory for any files that we save
10. * @param multiPartRequest Our MultiPartRequest object
11. */
12. public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir) {
13. super(request);
14.
15. multi = multiPartRequest;
16. try {
17. multi.parse(request, saveDir);
18. for (Object o : multi.getErrors()) {
19. String error = (String) o;
20. addError(error);
21. }
22. } catch (IOException e) {
23. addError("Cannot parse request: "+e.toString());
24. }
25. }
public class MultiPartRequestWrapper extends StrutsRequestWrapper {
protected static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestWrapper.class);
Collection<String> errors;
MultiPartRequest multi;
/**
* Process file downloads and log any errors.
*
* @param request Our HttpServletRequest object
* @param saveDir Target directory for any files that we save
* @param multiPartRequest Our MultiPartRequest object
*/
public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir) {
super(request);
multi = multiPartRequest;
try {
multi.parse(request, saveDir);
for (Object o : multi.getErrors()) {
String error = (String) o;
addError(error);
}
} catch (IOException e) {
addError("Cannot parse request: "+e.toString());
}
}
可以看到在构造的时候,调用multi.parse(request, saveDir)把上传的数据给封装了。这个MultiPartRequest的解析功能是在struts-default.xml中配置的,如下:
view plaincopy to clipboardprint?
01.<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default"/>
02.!-- 文件解析器类 -->
03.<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" />
04.
05.!-- 这就是struts2的文件解析器设置 -->
06.<constant name="struts.multipart.handler" value="jakarta" />
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default"/>
<!-- 文件解析器类 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" />
<!-- 这就是struts2的文件解析器设置 -->
<constant name="struts.multipart.handler" value="jakarta" />
现在的设想是,strut2不要帮我解析上传的文件,留到action中,我自己设置。所以我们要覆盖这是配置,如下:
view plaincopy to clipboardprint?
01.<!-- 重写文件上传解析方法 -->
02.<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="myRequestParser"
03.class="com.*.*.utils.MyRequestParseWrapper" scope="default" optional="true" />
04.
05.;constant name="struts.multipart.handler" value="myRequestParser" />
<!-- 重写文件上传解析方法 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="myRequestParser"
class="com.*.*.utils.MyRequestParseWrapper" scope="default" optional="true" />
<constant name="struts.multipart.handler" value="myRequestParser" />
这个MyRequestParseWrapper如下:
view plaincopy to clipboardprint?
01./**
02. * 重写struts2的request封装类
03. *
04. * @author scott.Cgi
05. */
06.public class MyRequestParseWrapper extends JakartaMultiPartRequest {
07. /*
08. * (non-Javadoc)
09. * @see
10. * org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest#parse
11. * (javax.servlet.http.HttpServletRequest, java.lang.String)
12. */
13. @Override
14. public void parse(HttpServletRequest servletRequest, String saveDir)
15. throws IOException {
16. //什么也不做
17. }
18.}
/**
* 重写struts2的request封装类
*
* @author scott.Cgi
*/
public class MyRequestParseWrapper extends JakartaMultiPartRequest {
/*
* (non-Javadoc)
* @see
* org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest#parse
* (javax.servlet.http.HttpServletRequest, java.lang.String)
*/
@Override
public void parse(HttpServletRequest servletRequest, String saveDir)
throws IOException {
//什么也不做
}
}
这样一来,在action中拿到的request就是带有上传文件的了。
接下来,所以说实现原理,依然使用common-uplaod.jar组件:
1. 页面有2个iframe,一个上传数据,一个显示进度。
2. 当然有2个action,一个上传数据,一个回写进度。
3. 上传的时候首先请求的是更新进度的iframe, 这个iframe执行客户端js发起上传文件请求,第二个iframe开始上传数据。与此同时,第一个iframe开始回写进度。进度对象保存在session中,通过request的hashcode为key。进度从第一个进度iframe,传递到第二个上传iframe中,实现进度信息的通信。
说明一下,2个iframe是因为,request未结束,不可以向客户端写数据,所以文件超大,就会阻塞回写信息。
具体的上传我封装了一下,具体代码如下:
view plaincopy to clipboardprint?
01.import java.io.File;
02.import java.io.FileOutputStream;
03.import java.io.IOException;
04.import java.io.InputStream;
05.import java.io.PrintWriter;
06.import java.util.List;
07.import javax.servlet.http.HttpServletRequest;
08.import javax.servlet.http.HttpServletResponse;
09.import org.apache.commons.fileupload.FileItem;
10.import org.apache.commons.fileupload.ProgressListener;
11.import org.apache.commons.fileupload.disk.DiskFileItemFactory;
12.import org.apache.commons.fileupload.servlet.ServletFileUpload;
13.import org.apache.log4j.Logger;
14./**
15. * upload file
16. *
17. * @author scott.Cgi
18. */
19.public class UploadFile {
20. private static final Logger LOG = Logger.getLogger(UploadFile.class);
21. /**
22. * 上传文件
23. *
24. * @param request
25. * http request
26. * @param response
27. * htp response
28. * @throws IOException
29. * IOException
30. */
31. @SuppressWarnings("unchecked")
32. public static void upload(HttpServletRequest request,
33. HttpServletResponse response) throws IOException {
34. LOG.info("客户端提交类型: " + request.getContentType());
35. if (request.getContentType() == null) {
36.