文件的上传下载
文件的上传下载
文件上传的配置
1)上传文件时表单前置条件
须把 HTML 表单的 enctype 属性设置为 multipart/form-data
须把 HTML 表单的method 属性设置为 post
需添加 字段.
2)Struts2的文件上需要使用FileUpload拦截器
上传一个文件示例
1.创建jsp展示页面
<s:form action="testUpload.action" method="post" enctype="multipart/form-data">
<s:file name="ppt" label="PPTFile"></s:file>
<s:textfield name="pptDesc" label="PPTDesc"></s:textfield>
<s:submit></s:submit>
</s:form>
2.Struts2 的文件上传实际上使用的是 Commons FileUpload 组件, 所以需要导入
commons-fileupload-1.3.jar
commons-io-2.0.1.jar
3.创建actin类
public class UploadAction extends ActionSupport{
/*省略get和set方法,还有构造器*/
private static final long serialVersionUID = 1L;
private File ppt;
private String pptContentType;
private String pptFileName;
private String pptDesc;
public String execute() throws Exception{
System.out.println(ppt);
System.out.println(pptContentType);
System.out.println(pptFileName);
System.out.println(pptDesc);
ServletContext servletContext=ServletActionContext.getServletContext();
String dir=servletContext.getRealPath("/files/"+pptFileName);
System.out.println(dir);
FileOutputStream out=new FileOutputStream(dir);
FileInputStream in=new FileInputStream(ppt);
byte[] buffer=new byte[1024];
int len=0;
while((len=in.read(buffer))!=-1) {
out.write(buffer,0,len);
}
out.close();
in.close();
return super.execute();
}
}
上传多个文件示例
1.action核心代码
public String execute() throws Exception{
ServletContext servletContext=ServletActionContext.getServletContext();
String dir=null;
//servletContext.getRealPath("/files/"+pptFileName);
System.out.println(dir);
FileOutputStream out=null;
//new FileOutputStream(dir);
FileInputStream in=null;
//new FileInputStream(ppt);
byte[] buffer;
Iterator<String> iterator = pptFileName.iterator();
Iterator<File> iterator2 = ppt.iterator();
while (iterator.hasNext()) {
dir=servletContext.getRealPath("/files/"+iterator.next());
out=new FileOutputStream(dir);
in=new FileInputStream(iterator2.next());
buffer=new byte[1024];
int len=0;
while((len=in.read(buffer))!=-1) {
out.write(buffer,0,len);
}
out.close();
in.close();
}
return super.execute();
}
2.页面输入
<s:form action="testUpload.action" method="post" enctype="multipart/form-data">
<s:file name="ppt" label="PPTFile"></s:file>
<s:textfield name="pptDesc" label="PPTDesc"></s:textfield>
<s:file name="ppt" label="PPTFile"></s:file>
<s:textfield name="pptDesc[0]" label="PPTDesc"></s:textfield>
<s:submit></s:submit>
</s:form>
上传文件参数配置
可以通过配置 FileUploadInterceptor 拦截器的参数的方式来进行限制
maximumSize (optional) - 默认的最大值为 2M. 上传的单个文件的最大值
allowedTypes (optional) - 允许的上传文件的类型. 多个使用 , 分割
allowedExtensions (optional) - 允许的上传文件的扩展名. 多个使用 , 分割.
注意: 在 org.apache.struts2 下的 default.properties 中有对上传的文件总的大小的限制. 可以使用常量的方式来修改该限制
struts.multipart.maxSize=2097152
例子:
<interceptors>
<interceptor-stack name="atguigustack">
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">2000</param>
<param name="fileUpload.allowedTypes">text/html,text/xml</param>
<param name="fileUpload.allowedExtensions">html,dtd,xml</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="atguigustack"></default-interceptor-ref>
定制错误消息
可以在国际化资源文件中定义如下的消息:
struts.messages.error.uploading - 文件上传出错的消息
struts.messages.error.file.too.large - 文件超过最大值的消息
struts.messages.error.content.type.not.allowed - 文件内容类型不合法的消息
struts.messages.error.file.extension.not.allowed - 文件扩展名不合法的消息
问题: 此种方式定制的消息并不完善. 可以参考 org.apache.struts2 下的 struts-messages.properties, 可以提供更多的定制信息.
文件的下载
1). Struts2 中使用 type=“stream” 的 result 进行下载即可
2). 具体使用细节参看 struts-2.3.15.3-all/struts-2.3.15.3/docs/WW/docs/stream-result.html
3). 可以为 stream 的 result 设定如下参数
contentType: 结果类型
contentLength: 下载的文件的长度
contentDisposition: 设定 Content-Dispositoin 响应头. 该响应头指定接应是一个文件下载类型, 一般取值为 attachment;filename=“document.pdf”.
inputName: 指定文件输入流的 getter 定义的那个属性的名字. 默认为 inputStream
bufferSize: 缓存的大小. 默认为 1024
allowCaching: 是否允许使用缓存
contentCharSet: 指定下载的字符集
文件下载示例
1.编写显示页面
<a href="testDownLoad.action">Down Load</a>
2.编写action方法
public class DownLoadAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private String contentType;
private long contentLength;
private String contentDisposition;
private InputStream inputStream;
public String execute() throws Exception {
contentType="text/html";
contentDisposition="attachment;filename=hidden.html";
ServletContext servletContext=ServletActionContext.getServletContext();
String fileName=servletContext.getRealPath("/files/hidden.html");
inputStream=new FileInputStream(fileName);
contentLength=inputStream.available();
return "sucess";
}
}
3.struts.xml配置
<action name="testDownLoad" class="com.atguigu.struts2.upload.app.DownLoadAction" method="execute">
<result name="sucess" type="stream">
<param name="bufferSize">2048</param>
</result>
</action>
表单的重复提交
在不刷新表单页面的前提下:
多次点击提交按钮
已经提交成功, 按 “回退” 之后, 再点击 “提交按钮”.
在控制器响应页面的形式为转发情况下,若已经提交成功, 然后点击 "刷新(F5)“
重复提交的缺点:
加重了服务器的负担
可能导致错误操作.
Struts2 解决表单的重复提交问题
I. 在 s:form 中添加 s:token 子标签
生成一个隐藏域
在 session 添加一个属性值
隐藏域的值和 session 的属性值是一致的.
若一致,移除session 的属性值
II. 使用 Token 或 TokenSession 拦截器.
这两个拦截器均不在默认的拦截器栈中, 所以需要手工配置一下
若使用 Token 拦截器, 则需要配置一个 token.valid 的 result
若使用 TokenSession 拦截器, 则不需要配置任何其它的 result
III. Token VS TokenSession
都是解决表单重复提交问题的
使用 token 拦截器会转到 token.valid 这个 result
使用 tokenSession 拦截器则还会响应那个目标页面, 但不会执行 tokenSession 的后续拦截器. 就像什么都没发生过一样!
IV. 可以使用 s:actionerror 标签来显示重复提交的错误消息.
该错误消息可以在国际化资源文件中覆盖. 该消息可以在 struts-messages.properties 文件中找到
struts.messages.invalid.token=^^The form has already been processed or no token was supplied, please try again.
示例代码
1.创建展示页面
<s:form action="testToken.action">
<s:token></s:token>
<s:textfield name="username" label="Username"></s:textfield>
<s:submit></s:submit>
</s:form>
2.action代码如下:
public class TokenAction extends ActionSupport{
private String username;
private static final long serialVersionUID = 1L;
public String execute() throws Exception {
Thread.sleep(2000);
return "sucess";
}
}
3.struts.xml配置如下:
<action name="testToken" class="com.atguigu.struts2.upload.app.TokenAction" method="execute">
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result name="sucess">/sucess.jsp</result>
<result name="invalid.token">/token-error.jsp</result>
</action>
4.输出返回配置如下:
<s:actionerror/>
5.国际化返回配置:
struts.messages.invalid.token=The form has already been processed or no token was supplied, please try again.
TokenSession配置如下:
<action name="testToken" class="com.atguigu.struts2.upload.app.TokenAction" method="execute">
<interceptor-ref name="tokenSession"></interceptor-ref>
<result name="sucess">/sucess.jsp</result>
</action>
自定义拦截器
拦截器需要实现Interceptor接口,需要实现三个基本方法:
init: 该方法将在拦截器被创建后立即被调用, 它在拦截器的生命周期内只被调用一次. 可以在该方法中对相关资源进行必要的初始化
interecept: 每拦截一个请求, 该方法就会被调用一次.
destroy: 该方法将在拦截器被销毁之前被调用, 它在拦截器的生命周期内也只被调用一次.
I. 定义一个拦截器的类
可以实现 Interceptor 接口
继承 AbstractInterceptor 抽象类
II. 在 struts.xml 文件配置.
III. 注意: 在自定义的拦截器中可以选择不调用 ActionInvocation 的 invoke() 方法. 那么后续的拦截器和 Action 方法将不会被调用.
Struts 会渲染自定义拦截器 intercept 方法返回值对应的 result
配置示例
1.创建自定义拦截器
public class MyInterceptor extends AbstractInterceptor{
private static final long serialVersionUID = 1L;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("before invocation.invoke...");
String result=invocation.invoke();
System.out.println(result);
System.out.println("after invocation.invoke...");
return result;
}
}
2.配置文件
<interceptors>
<interceptor name="hello" class="com.atguigu.struts2.upload.app.MyInterceptor"></interceptor>
</interceptors>
<action name="testToken" class="com.atguigu.struts2.upload.app.TokenAction" method="execute">
<interceptor-ref name="hello"></interceptor-ref>
<interceptor-ref name="tokenSession"></interceptor-ref>
<result name="sucess">/sucess.jsp</result>
</action>