struts2中,拦截器是非常核心的内容,框架默认提供的拦截器,我们可以从struts2-core-**.jar/struts-default.xml中查询到,我们以struts2-core-2.3.15.3.jar为例,打开struts-default.xml,可以看到如下的默认拦截器:
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
<interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
<interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
<interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/>
<interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" />
<interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
<interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
<interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
<interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
<interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
<interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
<interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/>
<interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
<interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
<interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
<interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
<interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
<interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
<interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
<interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
<interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" />
<interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" />
<interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" />
<interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" />
<interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" />
在struts-default.xml中,除了定义了一些默认的拦截器以外,还有一些默认的拦截器堆。这也提示我们,不光可以在使用时引用一个个的拦截器,也可以使用拦截器堆的方式,一次引用多个拦截器。如:我们在struts.xml配置文件中,没有配置拦截器的情况下,会默认引用以下这个拦截器堆:
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
</interceptor-stack>
为何我如此肯定,因为在这个文件中,有以下代码,我们在struts.xml中定义一个package元素时,通过也会extends这个struts-default.xml,那么也表示我们引用这个拦截器堆:
<default-interceptor-ref name="defaultStack"/>
好了,抄了一阵struts的源码,现在我们开始今天的事情:文件上传
先来进行单文件上传:
从上面的默认拦截器定义,可以看到,struts2给我们提供了一个现成的拦截器fileUpload,并且已经默认引用了。
我们先来做一些准备工作,做一个文件上传的页面,相信大家都还记得文件上传的页面必须满足以下三个条件:
- 表单的提交方式必须是post
- 表单的enctype必须为multipart/form-data
- 表单中必须提供一个type=file的input元素
以下就是这个表单提交的页面:
<fieldset>
<legend>单文件上传</legend>
<s:form action="single/upload.action" enctype="multipart/form-data" method="post">
<s:textfield name="name" label="姓名"></s:textfield>
<s:file name="photo" label="靓照"></s:file>
<s:submit value="上传"></s:submit>
</s:form>
</fieldset>
上面使用的是struts2的标签,当然也可以使用普通的html标签。
上面的表单提交,我们需要一个action动作在来支持,我们先来在struts.xml中配置一下这个action动作
<package name="upload" extends="struts-default" namespace="/single">
<action name="upload" class="demo.action.SingleFileUploadAction" method="upload">
<result>/success.jsp</result>
</action>
</package>
那个成功的返回页面success.jsp,没有任何展示,大不了就提示一下成功了
<body>
上传成功了
</body>
现在,我们就只差一个动作类了,我们来写配置文件中配置的demo.action.SingleFileUploadAction这个动作类:
package demo.action;
//省略引入的包
/**
* 这个测试类,简单起见,使用模型和动作为同一个类的方式
* @author Minhellic
*
*/
public class SingleFileUploadAction extends ActionSupport {
private String name; //对应页面上的name字段
private File photo; //对应页面上要上传的文件,必须使用File来接收
private String photoFileName; //上传的文件名,XXXFileName这样的固定写法
private String photoContentType; //上传文件的MIME类型, XXXContentType这样的固定写法
public String upload() throws IOException {
//普通的属性,可以直接使用
System.out.println(name);
/*
* 完全文件上传
*/
//1.得到文件上要传的真实路径
ServletContext sc = ServletActionContext.getServletContext();
String dirPath = sc.getRealPath("/files");//得到文件的真实保存路径,需要在WebRoot根目录下建一个files目录
//2.构建目标文件
File target = new File(dirPath, photoFileName);
//3.复制文件
FileUtils.copyFile(photo, target);
return SUCCESS;
}
//省略各个属性的getter和setter
}
以上代码为了简洁,省略了导入的包和各个属性的getter和setter,大家可以自己添加上。
通过上面的代码可以看到,我们需要有一个文件夹来存放上传的文件,本例,我们是在工程的根目录下,用一个files文件夹来存放的,所以,我们在WebRoot下,建一个files文件夹
好了,最最粗糙的文件上传完成了,先来看看效果
部署到tomcat,访问这个上传页面:
随便选张图片,上传下,果然,上传成功了。我们到tomcat下去查看,在项目的根目录下,发现有一个files目录,打开这个目录,发现确实上传成功了
感觉是完成了。再来得瑟一下,这回我选择一个视频,大概有几十M吧,然后
我操,这一定不是真的,刚才玩得还好好的。关键是,控制台还没报错,这怎么办?仔细看看这个报错,发现报的是没有input对应的逻辑视图,可明明我们的action里只返回了success,哪来的input,除了这个action,就只有一个可能,就是那个fileUpload拦截器干的。
好吧,既然你说我没有input视图,那我给你一个,在struts.xml中,action的定义里,添加一个result元素
<result name="input">/index.jsp</result>
再次去尝试去上传一个那个几十M的视频文件,这回倒是不报错了,而是重新又回到了上传页面。
那么问题来了,为什么呢?再上传那张图片是可以成功的,为什么视频就不可以吗?是因为格式不对吗?也没有指定格式呀,按理说应该是可以支持所有格式的,毕竟只是一个上传,又不是解析。
那会不会是大小的问题呢,可能性很大。因为文件上传不成功,无非不是路径问题,文件格式问题,然后就是文件大小问题,前面已经成功过,那么路径是没有问题的,格式的问题可能性很小。那么我们来找找有没有设置文件默认上传大小的地方:
我们知道,struts2加载的时候,不单会加载struts-default.xml,还会加载一个叫default.properties的文件,我们在struts2-core-2.3.15.3.jar包中,找到这个文件,仔细一看,找到了:
struts.multipart.maxSize=2097152
原来这里的文件默认大小这么小,难怪上传不上去,好吧,我们可以在struts.xml中,通过constant元素,来修改这个默认值
<constant name="struts.multipart.maxSize" value="100000000"></constant>
再次部署,试一下,终于成功了。在tomcat中,也找到了这个文件
感觉像是应该没有问题了。然而如果把这样的上传页面交付给甲方,相信这项目一定就没希望了。我们想想,这个地方明明在页面上写的是要上传“靓照”的,那也就是说这里我们希望要上传的是一张图片。可用户可不管那么多,他什么都有可能上传,就像我刚才一样,居然会上传一个视频上来。而且,如果用户上传的文件不对,应该给出提示。
那么,下面我们就来限制一下用户上传文件的大小和后缀名,如果用户上传的东西不对,那么给用户一个提示。
文件大小的限制,上面已经介绍了,那么现在,就应该是要给出用户提示了。
为了方便测试,我先把上传大小的限制调小一些,这样基本上随便一个文件都会过大,这样好测试:
<constant name="struts.multipart.maxSize" value="1000"></constant>
struts2提供了一些标签,可以用于展示这些提示信息,比如这次的错误提示,我们就可以在页面上用以下标签展示:
<s:actionerror/>
这样,我们再次上传一个超过大小的文件,则会在页面上出现以下提示:
至于要把这个提示展示成中文的,则是可以通过国际化的配置来做,就不再是这篇博客要表现的东西了。