最近在做一个文件上传中显示进度条的功能,也在网上搜了下例子还挺多的包括JSP版和SERVLET版的。这几个版本都比较好实现,但是struts2版本的进度条非常少,有个把例子但是也运行不了。但是公司用的又是S2,开始按照JSP版的弄了一个晚上还是无法实现。无奈之下只能睡觉了,睡觉的时候想到了S2文件的上传到action开始执行这一步的时候其实文件已经在服务器上了,action要做的就是将文件复制到目的地址。因此文件上传的所有工作都在S2中的拦截器中完成的。就是FileUploadInterceptor拦截器,由于是封装好了的,因此决定第二天开始研究S2的文件上传源码。
第二天研究了FileUploadInterceptor拦截器发现该类并不做任何IO的操作,因此文件的上传还不是在该类中处理的,要再往下层研究。最后找出了S2实现的关系图也是S2的核心实现。如下图所示:
发现文件的上传工作在类JakartaMultiPartRequest中进行的。只要在该类中做点文章就能够对数据流的监控了。但是问题又来了,如果用一个改过的类替换掉JakartaMultiPartRequest就改动了S2的包了,觉得这样的做法并不方便。应该找到能够在外部进行动态的配置比较好。
接着我想到了所有的请求都要经过FilterDispatcher这个类,通过这个类匹配不同的拦截器。通过源代码最后找到了ConfigurationManager类,系统是由该类来创建Dispatcher的,它需要xwork.xml配置文件的配置动态的创建Dispatcher。到这里我就想到了S2包下的配置文件,这里有个默认的配置文件:struts-default.xml。系统也会通过这个文件加载一些默认的信息。结果找到了以下的配置:
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" optional="true"/>
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" optional="true" />
这就意味着可以作较少的改动就能够替换掉JakartaMultiPartRequest类了,只要我写一个实现了MultiPartRequest接口就可以了。
当然S2的文件上传还要其他的一些外部类,上面只是针对S2的一个切入点。
以及以下的这些包:(demo中加入了DWR框架进行异步通信进度条效果更好)
因此新建一个GarryMultiPartRequest类实现了MultiPartRequest接口;GarryMultiPartRequest类的方法与JakartaMultiPartRequest完全一样只要修改parse()方法即可——添加一个IO流的Listener就可以监听文件上传状态了。如下图:
代码清单:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<html>
<head>
<title>Simple upload page</title>
<script src='resources/js/upload.js'> </script>
<script src='<%=request.getContextPath() %>/dwr/interface/UploadMonitor.js'> </script>
<script src='<%=request.getContextPath() %>/dwr/engine.js'> </script>
<script src='<%=request.getContextPath() %>/dwr/util.js'> </script>
</head>
<body>
<form action="<%=request.getContextPath() %>/fileUpload.action" enctype="multipart/form-data" method="post"
onsubmit="startProgress()">
<h1>
Web upload
</h1>
<input class="default" type="file" id="file1" name="excel" />
<br />
<input type="submit" value="begin upload" id="uploadbutton" />
<br />
<div id="progressBar" style="display: none;">
<div id="theMeter">
<div id="progressBarText"></div>
<div id="progressBarBox">
<div id="progressBarBoxContent"></div>
</div>
</div>
</div>
</form>
</body>
</html>
Dwr.xml
<dwr>
<allow>
<create creator="new" javascript="UploadMonitor">
<param name="class" value="be.telio.mediastore.ui.upload.UploadMonitor"/>
</create>
<convert converter="bean" match="be.telio.mediastore.ui.upload.UploadInfo"/>
</allow>
</dwr>
Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>Ajax Upload</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>logLevel</param-name>
<param-value>WARN</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<!-- The Usual Welcome File List -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
最后运行项目的时候还是失败了,系统并没有使用我写的GarryMultiPartRequest类,问题出一定出在了struts-default.xml文件的配置上了。最后发现了一个更加灵活的配置方法,不用更改S2的jar包文件。在struts.xml里添加以下的配置:
<constant name="struts.multipart.parser" value="xxx.xxx.GarryMultiPartRequest" />
这样系统才会调用自定义的GarryMultiPartRequest类。
系统运行后效果如图所示:
运行一切正常!因此我的思路是对的,也得到一个结论S2的action只是辅助的组件,如果要深入的了解S2应该好好看下interceptor包下的类,毕竟S2以interceptor为主。
相关源码已经放到了CSDN下载区了