文件的上传和下载
文件的上传
必要前提:
表单的method必须是post
表单的enctype必须是multipart/form-data
提供input type=”file”类型上传输入域
在Struts2中,文件上传是由一个叫做fileUpload拦截器完成的。
上传页面
<body>
<s:actionerror/><!-- 显示错误信息 -->
<s:form action="upload" namespace="/uploadFile" enctype="multipart/form-data" method="post">
<!-- name属性一定要和动作类中的属性名一致 -->
<s:file name="photo" label="文件1" ></s:file>
<s:file name="photo" label="文件2" ></s:file>
<s:submit value="上传"></s:submit>
</s:form>
</body>
配置信息
<!-- 限制上传文件的总大小 -->
<constant name="struts.multipart.maxSize" value="10485760"></constant>
<!-- 国际化加载资源信息,用来回显上传出错的错误信息,fileupload为信息配置文件的名字 -->
<constant name="struts.custom.i18n.resources" value="fileupload"></constant>
<!-- 文件上传 -->
<package name="uploadFile" extends="struts-default" namespace="/uploadFile">
<action name="upload" class="com.jyh.action.Upload1Action" method="upload">
<!-- 引用defaultStack默认的拦截器栈 -->
<interceptor-ref name="defaultStack">
<!-- 设置文件的MIME类型 -->
<param name="fileUpload.allowedTypesSet">image/jpeg,image/bmp</param>
<!-- 设置该栈中fileUpload拦截器的allowedExtensions属性值(允许上传文件的类型,即后缀名 )-->
<param name="fileUpload.allowedExtensions">.jpg,.bmp,.png</param>
</interceptor-ref>
<result name="success">/success.jsp</result>
<result name="input">/upload.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
动作类
package com.jyh.action;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletContext;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class Upload1Action extends ActionSupport {
private String name;
private File[] photo;//多文件上传,用数组或者集合
private String[] photoFileName;//上传文件的文件名,xxxFileName固定写法
private String[] photoContentType;//上传文件的MIME类型,xxxContentType固定写法
public String upload() {
ServletContext context = ServletActionContext.getServletContext();
String directory = context.getRealPath("/files");
if(StringUtils.isBlank(photoFileName[0])){
return INPUT;
}else{
for (int i = 0; i < photo.length; i++) {
File target = new File(directory, photoFileName[i]);
try {
FileUtils.copyFile(photo[i], target);//将源文件复制到上传目录
} catch (IOException e) {
return INPUT;
}
}
}
return SUCCESS;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public File[] getPhoto() {
return photo;
}
public void setPhoto(File[] photo) {
this.photo = photo;
}
public String[] getPhotoFileName() {
return photoFileName;
}
public void setPhotoFileName(String[] photoFileName) {
this.photoFileName = photoFileName;
}
public String[] getPhotoContentType() {
return photoContentType;
}
public void setPhotoContentType(String[] photoContentType) {
this.photoContentType = photoContentType;
}
}
错误信息配置
文件的下载:结果类型的使用
配置信息
<package name="downloadFile" extends="struts-default" namespace="/downloadFile">
<action name="download" class="com.jyh.action.DownloadAction" method="download">
<!-- type结果类型为stream -->
<result name="success" type="stream">
<!-- 指定是动作类中的哪个属性 -->
<param name="inputName">inputStream</param>
<!-- 通知浏览器以下载的方式打开 -->
<param name="contentDisposition">attachment;filename=${fileName}</param>
<!-- MIME类型 -->
<param name="contentType">application/octet-stream</param>
</result>
<result name="error">/error.jsp</result>
</action>
</package>
动作类
package com.jyh.action;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import org.apache.commons.io.FilenameUtils;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class DownloadAction extends ActionSupport {
private InputStream inputStream;
private String fileName;
public String download(){
try {
String realPath = ServletActionContext.getServletContext().getRealPath("/files/" + fileName);
inputStream = new FileInputStream(realPath);
} catch (Exception e) {
return ERROR;
}
return SUCCESS;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
Struts中的数据中心与OGNL表达式
Stack Context,Value Stack Contents(root),ValueStack和ActionContext之间的关系
key | value | 备注 |
---|---|---|
request | ServletRequest中的所有属性(Attributes) | 相当于EL表达式中的requestScope |
session | HttpSession中的所有Attributes | 相当于EL表达式中的sessionScope |
application | ServletContext中的所有Attributes | 相当于EL表达式中的applicationScope |
action | 动作类 | |
value stack(根) | 一个List栈 | |
parameters | request.getParameterMap(),请求中的参数 | 相当于EL表达式中的paramValues |
attr | 按照request>session>application的顺序查找数据 | ${} |
OGNL表达式简介
前提:在struts2中要使用OGNL表达式,必须放到Struts2的标签中。
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,
它是一个开源项目。webwork用它作为表达式语言。
功能:
.支持对象方法调用,如xxx.doSomeSpecial();
2.支持类静态的方法调用和值访问;
<body>
<!-- s:property 就相当于JSTL中的 c:out -->
<!-- 调用任意对象的任意方法 -->
<s:property value="'abcdefg'.length()"/>
<hr/>
<!-- 访问静态变量 -->
<s:property value="@java.lang.Integer@MAX_VALUE"/>
<hr/>
<!-- 访问静态方法 配置参数:<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>-->
<s:property value="@java.lang.Math@abs(-100)"/>
<hr/>
</body>
3.访问OGNL上下文(OGNL context)和ActionContext;
java代码添加数据
package com.jyh.action;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.ValueStack;
@SuppressWarnings("serial")
public class Demo1Action extends ActionSupport {
private String userName = "哈哈哈";
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String m1(){
ActionContext actionContext = ActionContext.getContext();//action
ValueStack vs = actionContext.getValueStack();//根栈
Map<String, Object> contextMap = vs.getContext();//上下文Map
//验证
actionContext.put("p1", "pppp1");
String s1 = (String)contextMap.get("p1");
contextMap.put("p2", "pppp2");
String s2 = (String)actionContext.get("p2");
System.out.println("s1:" + s1 + ",s2:" + s2);
//得出结论:actionContextput进去的数据contextMap可以取出来,反之亦可,
//查看源码可知,actionContextput和get方法就是调用contextMap的put和get方法
vs.set("p3", "pp3");//检测栈顶中有没有map,没有就创建一个map,把本值塞入map中后再将map push进去
vs.set("p4", "p4");//检测到栈顶是一个map,直接将数据塞入到 map中去
vs.setValue("p4", "pp4");//从栈顶开始搜索p4,找到之后就调用setAbc方法设置值
vs.setValue("#p5", "p5");//添加进contextMap中去
Object obj = vs.findValue("p3");//查找栈中的值,如果栈中找不到,就将值当做key在Stack Context中找
System.out.println(obj.toString());
Object obj2 = vs.findValue("#p5");
System.out.println(obj2.toString());
//-----------------------------
CompoundRoot root = vs.getRoot();//contextMap中的根
//System.out.println(root);
//压对象进栈
vs.push(new Date());
vs.push("d1");
System.out.println("d1:" + root.peek());
root.push("d2");
System.out.println("d2:" + vs.peek());
//压两个时间对象
Calendar c1 = Calendar.getInstance();
c1.set(2000, 0, 1);
vs.push(c1.getTime());//2000-1-1
Calendar c2 = Calendar.getInstance();
c2.set(2014, 10,2);
vs.push(c2.getTime());//2014-11-2
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("r1", "r1");//将值塞入request域中去
actionContext.put("r2", "r2");//将值塞入Stack Context中去
return SUCCESS;
}
public String regist(){
System.out.println(userName);
return null;
}
public String regist1(){
System.out.println(userName);
return SUCCESS;
}
}
页面用OGNL表达式获取数据
<body>
<s:debug></s:debug>
<!-- 取根栈中对象的属性 -->
<s:property value="time"/><hr>
<s:property value="userName"/><hr>
<!-- 取第二个时间的月份 -->
<s:property value="[1].month"/><hr>
<!-- #表示获取Stack Context中的数据 -->
<s:property value="#p1"/><br>
<s:property value="p1"/><hr>
<s:property value="p3"/><br>
<s:property value="p4"/><hr>
<s:property value="#p5"/><br>
<s:property value="p5"/><hr>
<s:property value="#request.r1"/><br>
${r1}<br>
<s:property value="r2"/><br>
${r2}<hr>
</body>
4.操作集合对象。
<s:property value="{'a','b','c'}.getClass()"/><br/>
<s:property value="#{'1':'a','2':'b','3':'c'}.getClass()"/><br/>
<s:radio name="gender" list="#{'0':'男','1':'女'}"></s:radio><br/>
<s:checkboxlist name="hobbies" list="{'吃饭','睡觉','打逗逗'}"></s:checkboxlist><br/>
5.字符串和OGNL表达式转换
<!-- 字符串转OGNL表达式,%{}括起来 -->
<s:textfield name="姓名" label="%{姓名}"></s:textfield><br/>
<!-- OGNL表达式转字符串,引号 包含起来 (property标签的value属性都是OGNL表达式 )-->
<s:property value="'0'"/>
6.在配置文件中xml或properties也可以使用OGNL表达式,请使用${ognl}
<!-- 通知浏览器以下载的方式打开 -->
<param name="contentDisposition">attachment;filename=${fileName}</param>
Struts2中的常用标签
<body>
<!-- 输出标签 -->
<s:property value="{'a','b','c'}.getClass()"/><br/>
<s:property value="#{'1':'a','2':'b','3':'c'}.getClass()"/><br/>
<!-- radio标签 -->
<s:radio name="gender" list="#{'0':'男','1':'女'}"></s:radio><br/>
<!-- 相当于checkbox -->
<s:checkboxlist name="hobbies" list="{'吃饭','睡觉','打逗逗'}"></s:checkboxlist><br/>
<!-- 相当于input -->
<s:textfield name="姓名" label="%{姓名}"></s:textfield><br/>
<br/><hr><br/>
<!--
set标签,存数据,
范围有:application|request|session|action
默认范围是action,1.放到请求范围request和2.放到Stack Context中去
-->
<s:set value="'session'" var="ss" scope="session"></s:set><br/>
<s:property value="#session.ss"/>
<s:set value="'value'" var="key"></s:set><br/>
StackContext:<s:property value="#key"/><br/>
requestScope:<s:property value="#request.key"/><br/>
<!-- bean:new一个对象放入Stack Context中去 -->
<s:bean name="java.util.Date" var="now"></s:bean><!-- new一个名为now的时间对象放入StackContext中去 -->
<s:property value="#now.time"/><br>
<!-- 包含另一个动作 -->
<s:action name="demo2" executeResult="true"></s:action><br/>
<!-- 循环 -->
<table border="1">
<tr>
<td>key</td>
<td>value</td>
</tr>
<!-- 把当前遍历的元素压如StackContext中去,var的值就是所对应的key,var对应的value就是需要的键值对象 -->
<s:iterator value="#request" var="me">
<tr>
<td><s:property value="#me.key"/></td>
<td><s:property value="#me.value"/></td>
</tr>
</s:iterator>
<tr><td>---</td><td>---</td></tr>
<!-- 不指定var,将要遍历的数据压入根栈中去 -->
<s:iterator value="#request">
<tr>
<td><s:property value="key"/></td>
<td><s:property value="value"/></td>
</tr>
</s:iterator>
</table>
<br>
<!--
status存当前遍历的数据并放入StackContext,并提供以下方法
isOdd,isEven,isLast,isFirst,getIndex,getCount
-->
<table border="1">
<tr><td>key</td><td>value</td><td>index</td></tr>
<!-- 不指定var,将要遍历的数据压入根栈中去 -->
<s:iterator value="#request" status="s">
<tr>
<td><s:property value="key"/></td>
<td><s:property value="value"/></td>
<td><s:property value="#s.count"/></td>
</tr>
</s:iterator>
</table>
<!-- a标签链接 -->
<s:a action="stackDemo/demo1">戳这里<s:param name="userName" value="'不明'"></s:param></s:a>
<s:url action="stackDemo/demo1" var="url">
<s:param name="userName" value="'不明'"></s:param>
</s:url>
<a href="${url}">猛戳</a>
</body>
防止表单重复提交
表单页面
<body>
<s:form action="/form/formRegist1" method="post">
<s:textfield name="userName" label="姓名"></s:textfield>
<s:token></s:token><!-- 关键 -->
<s:submit value="注册"></s:submit>
</s:form>
</body>
配置信息
<!-- 防止表单重复提交 -->
<package name="form" extends="struts-default" namespace="/form">
<action name="formRegist" class="com.jyh.action.Demo1Action" method="regist">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="token"></interceptor-ref>
<!-- 拦截器转向,在表单重复提交的时候 -->
<result name="invalid.token">/msg.jsp</result>
</action>
<action name="formRegist1" class="com.jyh.action.Demo1Action" method="regist1">
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 与token不同的是,重复提交之后会返回提交的界面 -->
<interceptor-ref name="tokenSession"></interceptor-ref>
<result>/success.jsp</result>
</action>
</package>