(三)、Struts第三天
(三)、Struts第三天
Struts核心业务:
(Struts提供了哪些功能?)
1. 请求数据自动封装(params拦截器)
2. struts数据处理方式
* ActionContext
* 实现RequestAware 等接口 (servletConfig拦截器)
* ServletActionContext
3. 拦截器功能
4. Ognl表达式语言与Struts标签 (今天)
5. 文件上传 & 下载 (今天)
6. 数据效验
7. 类型转换、国际化
8 . 其他技术
模型驱动、数据回显等!
1. Ognl表达式语言与Struts标签
问题1:
Struts在运行时候,产生的数据,如果传递到页面,再jsp中取出显示的?
问题2:
Ognl表达式语言与Struts标签, 关系?
Ognl表达式概述:
Struts2默认 必须引入Ognl.jar。这个jar包就是对Ognl 表达式语言的支持!
l OGNL表达式
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。
Strut2标签取值使用的表达式,就是Ognl表达式!
问题:标签取值方式?
El 表达式语言
是jsp2.0以后官方默认支持标准!
Struts2标签:
在struts2环境支持时候才可以用!
(功能更加强大!)
l OGNL优势
1、支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问,表达式的格式:
@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo %s', 'bar')
或@tutorial.MyConstant@APP_NAME;
3、支持赋值操作和表达式串联,如price=100, discount=0.8,
calculatePrice(),这个表达式会返回80;
4、访问OGNL上下文(OGNL context)和ActionContext;
5、操作集合对象。
l 总结
OGNL 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了 java.utils.Map 的接口。 这个Map就是值:OgnlContext!
OgnlContext对象
OgnlContext对象,是Ognl表达式语言的核心!
注意:
一般情况,很少直接用这个对象! Struts2标签取值时候使用!
硬编码方式,来了解一下这个对象:
/** * 硬编码,了解OgnlContext对象! * @author AdminTH * */ public class OgnlContextDemo {
// 1. OgnlContext实际上是一个Map! @Test public void testApp() throws Exception { // 创建OgnlContext对象, 是一个Map! OgnlContext context = new OgnlContext(); // 往map中放数据 context.put("cn", "China"); // 获取map中数据 System.out.println(context.get("cn")); } // 2. OgnlContext对象,表达式取值:”取根元素值“ @Test public void testApp2() throws Exception { OgnlContext context = new OgnlContext(); // 往根元素设置值 context.setRoot(new User(100,"Jack", new Address())); // 通过Ognl表达式取值 // a. 构建一个表达式 Object ognl = Ognl.parseExpression("address.city"); // b. 解析表达式 Object value = Ognl.getValue(ognl, context, context.getRoot()); // 输出值 System.out.println(value); } // 3. OgnlContext对象,表达式取值:”取非根元素值“ // 总结: Ognl表达式语言取值,如果获取根元素值,直接写表达式; 非根元素值,在表达式前要加上# @Test public void testApp3() throws Exception { OgnlContext context = new OgnlContext(); // 往map里面放数据(也叫往“非根元素”存放数据) context.put("user", new User(100,"Jack", new Address())); // 通过Ognl表达式获取值 Object ognl = Ognl.parseExpression("#user.address.city"); Object value = Ognl.getValue(ognl, context, context.getRoot()); System.out.println(value); } //4. 其他功能,调用静态方法! @Test public void testApp4() throws Exception { OgnlContext context = new OgnlContext(); // 如果是Math类的静态方法调用,可以省略类名称! 其他类,就必须写上类名! // Object ognl = Ognl.parseExpression("@Math@floor(10.1)"); // 得到表达式 Object ognl = Ognl.parseExpression("@@floor(10.1)"); // 得到表达式 Object value = Ognl.getValue(ognl, context, context.getRoot());// 解析表达式,获取值 System.out.println(value); } } |
ValustStack值栈对象
问题:
OgnlContext对象与值栈对象的关系?
l ValueStack
ValueStack实际是一个接口
Struts2在运行时候,时候的是接口的实现类:OgnlValueStack!
l ValueStack特点
ValueStack贯穿整个 Action 的生命周期!
相当于一个数据的中转站. 在其中保存当前Action 对象和其他相关对象.
Strut2运行时候,产生的数据都会保存值栈中!
Struts中数据流转:
用户每次访问Struts的action,struts2都会创建:
1. ActionContext对象
ActionContext.getContext.getSessionMap(“key”);
这个数据就是从值栈获取的!
2. 值栈对象
3. Action对象
然后会把action对象放到值栈中,
且通过ActionContext是可以获取值栈数据的!
l 获取方式
Struts2框架把 ValueStack 对象保存在名为 “struts.valueStack” 的
request请求属性中。
方式1: 通过ActionContext可以获取
方式2: 通过request对象获取!
l 值栈结构
List栈: 存储Action对象!或其他根元素对象(Root) 【不用#】
Map栈: 存储映射数据!Map数据(非根元素数据) 【需要#符号】
requestMap.put(…);
Request.setAttribute(“”,””)
Struts2标签取值
// 查看值栈结构 public class VsAction extends ActionSupport{ private User user; public User getUser() { return user; }
@Override public String execute() throws Exception { // 模拟:调用Service查询! this.user = new User(1,"班长",new Address("骏景花园")); // 获取值栈,方式1: // ValueStack vs2 = // (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack"); // 获取值栈,方式2 : ActionContext ac = ActionContext.getContext(); ValueStack vs1 = ac.getValueStack();
/* * ValustStack: * * 1. 往根据元素root, list栈, 节点存放数据 【root CompoundRoot】 */ vs1.set("cn", "China"); // 存储以Map结构存储! vs1.set("usa", "Ameraica"); vs1.setValue("test", "test"); User user = new User(1,"杰克",new Address("广州")); vs1.push(user); // 入栈, 放入list栈中第一个元素 /* * 2. 往非给元素存放数据,map栈! 【context OgnlContext】 */ // 获取request/session/application的map Map<String,Object> request = (Map<String, Object>) ac.get("request"); Map<String, Object> session = ac.getSession(); Map<String, Object> application = ac.getApplication(); // 存数据 ac.getContextMap().put("test_contextMap", "test_contextMap"); request.put("request_data", "request_data"); request.put("request_data2", "request_data2"); session.put("session_data", "session_data"); application.put("application_data", "application_data"); return SUCCESS; } }
|
<body> <!-- 引入struts标签库 --> <%@taglib uri="/struts-tags" prefix="s" %> <!-- 一、 通过el, 获取struts运行产生的数据 --> ${user.id } ${user.name } ${user.address.city } ${requestScope.test_contextMap } ${requestScope.request_data } ${sessionScope.session_data } <hr/>
<!-- 二、使用Ognl表达式语言取值! (必须要使用struts标签!)--> <!-- property标签取值,默认获取栈顶元素的值 --> <s:property/> <s:property value="user.id"/> <s:property value="user.name"/> <s:property value="user.address.city"/> <hr/> <!-- 非根元素取值 --> <s:property value="#test_contextMap"/> <s:property value="#request.test_contextMap"/> <s:property value="#request.request_data"/> <hr/> <s:property value="#session.session_data"/> <s:property value="#application.application_data"/> <hr/> <!-- 使用attr取值 : 自动搜索request的Map、session的map、application的map,找到后立刻返回!--> <s:property value="#attr.request_data"/> <s:property value="#attr.session_data"/> <s:property value="#attr.application_data"/> <hr/> <!-- 封装请求数据值的map:parameters 访问:http://localhost:8080/day29/ognl.jsp?userName=Jack&pwd=888 --> <s:property value="#parameters"/> <s:property value="#parameters.userName"/> <!-- 输出:Jack --> <s:property value="#parameters.pwd"/> <!-- 输出:888 --> <!-- 查看值栈数据 --> <s:debug></s:debug> </body> |
取值中几个符号用法 & Struts2常用标签
Struts2中几个取值符号的用法:
$ 配置文件取值符号
# 非根元素取域中值; 动态构建Map集合!
% 提供一个Ognl表达式运行环境
|
|
5.文件上传 & 下载
文件上传
Struts2提供的文件上传,主要是简化传统的上传操作!
在引入struts2的jar包中,默认必须引入文件上传的2个jar包!
回顾文件上传:
客户端:
1. 提交方式:post
2. 表单类型: multipart/form-data
3. 文件域
服务器段:
a. 自己解析内容
b. apache提供的fileUpload组件!
c. 其他文件上传组件
Struts2如何简化上传操作?
ü 上传单个文件
Jsp页面: <body> <form action="/day29/fileUpload.action" method="post" enctype="multipart/form-data"> 用户名:<input type="text" name="userName"> <br/> 文件: <input type="file" name="file1"> <br/> <input type="submit"> </form> </body>
|
Action: package cn.itcast.c_upload;
import java.io.File; import java.io.IOException;
import org.apache.commons.io.FileUtils; import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport;
// 上传一个处理 public class UploadAction_上传一个 extends ActionSupport{
// 封装普通表单数据 private String userName; public void setUserName(String userName) { this.userName = userName; } // 封装文件上传数据 private File file1; // 上传的文件; 对象名称与表单元素名称一致 private String file1FileName; // 上传的文件名 = 元素名称+ FileName(固定) private String file1ContentType;// 文件类型 public void setFile1(File file1) { this.file1 = file1; } public void setFile1FileName(String file1FileName) { this.file1FileName = file1FileName; } public void setFile1ContentType(String file1ContentType) { this.file1ContentType = file1ContentType; } // 主要测试“上传”! @Override public String execute() { // 文件上传到服务器的: "/day27/upload目录" // 获取Actioncontext对象 ActionContext ac = ActionContext.getContext(); try { //1. 获取上传目录,路径 String path = ServletActionContext.getServletContext().getRealPath("/upload"); //2. 在目录下创建文件对象 File dest = new File(path,file1FileName); //3. 上传 // 参数1: 源文件 // 参数2: 上传到的目标文件 FileUtils.copyFile(file1, dest); // 提示 ac.put("msg", "上传" + file1FileName +"成功!"); } catch (Exception e) { ac.put("msg", "上传" + file1FileName +"失败!!"); e.printStackTrace(); } return SUCCESS; } } |
|
ü 上传多个文件
Jsp页面: <body> <form action="/day29/fileUpload.action" method="post" enctype="multipart/form-data"> 用户名:<input type="text" name="userName"> <br/> 文件: <input type="file" name="file1"> <br/> 文件: <input type="file" name="file1"> <br/> <input type="submit"> </form> </body>
|
package cn.itcast.c_upload;
import java.io.File; import java.util.ArrayList; import java.util.List;
import org.apache.commons.io.FileUtils; import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport;
// 上传多个 public class UploadAction extends ActionSupport{
// 封装普通表单数据 private String userName; public void setUserName(String userName) { this.userName = userName; } // 封装文件上传数据 private File[] file1; // 上传的文件; 对象名称与表单元素名称一致 private String[] file1FileName; // 上传的文件名 = 元素名称+ FileName(固定) private String[] file1ContentType;// 文件类型 public void setFile1(File[] file1) { this.file1 = file1; } public void setFile1FileName(String[] file1FileName) { this.file1FileName = file1FileName; } public void setFile1ContentType(String[] file1ContentType) { this.file1ContentType = file1ContentType; } // 主要测试“上传”! @Override public String execute() { // 文件上传到服务器的: "/day27/upload目录" // 获取Actioncontext对象 ActionContext ac = ActionContext.getContext(); // 提示信息保存 List<String> list = new ArrayList<String>(); try { //1. 获取上传目录,路径 String path = ServletActionContext.getServletContext().getRealPath("/upload"); // 上传多个,增加遍历功能! for (int i=0; i<file1.length; i++){ //2. 在目录下创建文件对象 File dest = new File(path,file1FileName[i]); //3. 上传 // 参数1: 源文件 // 参数2: 上传到的目标文件 FileUtils.copyFile(file1[i], dest); list.add("第"+(i+1)+"个文件:" +file1FileName[i] + ", 上传成功!"); } // 保存提示 ac.put("list", list); } catch (Exception e) { ac.put("msg", "上传失败!!"); e.printStackTrace(); } return SUCCESS; } }
|
ü 错误处理、大小限制、类型限制
错误处理:
<global-results> <!-- struts在文件上传失败的时候,会自动返回input视图,即错误页面对应的视图 --> <result name="input">/c/error.jsp</result> </global-results> |
大小、类型限制: <!-- 下载返回处理:stream --> <result name="down" type="stream"> <!-- 下载的文件的mime类型; 指定为二进制类型即可,支持所有文件类型;(tomcat/confg/web.xml搜索exe) --> <param name="contentType">application/octet-stream</param> <!-- 对应Action类中get属性名称!(即:action类返回流的方法!) --> <param name="inputName">fileStream</param> <!-- 指定下载的响应头; 以及下载显示的文件的文件名(中文,需要url编码后) --> <param name="contentDisposition">attachment;filename=${downFileName}</param> <!-- 每次读取文件的缓存大小 --> <param name="bufferSize">1024</param> </result> |
ü 完整的struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- 配置struts上传默认支持的大小限制是:20M, 默认是2M --> <constant name="struts.multipart.maxSize" value="20971520"></constant>
<package name="upload" extends="struts-default"> <global-results> <!-- struts在文件上传失败的时候,会自动返回input视图,即错误页面对应的视图 --> <result name="input">/c/error.jsp</result> </global-results> <action name="fileUpload" class="cn.itcast.c_upload.UploadAction"> <!-- 指定执行默认的拦截器,给fileUpload拦截器设置参数值 --> <interceptor-ref name="defaultStack"> <!-- 限制允许的文件扩展名称 --> <param name="fileUpload.allowedExtensions">txt,jar,zip,jpg</param> <!-- 限制允许的文件类型 (注意:如果与线面的扩展名名称限制一起使用,取交集!) <param name="fileUpload.allowedTypes">text/plain</param> --> </interceptor-ref> <result>/c/msg.jsp</result> </action> <!-- 下载的action --> <action name="down_*" class="cn.itcast.c_upload.DownAction" method="{1}"> <!--下载 列表 --> <result name="list">/c/down.jsp</result> <!-- 下载返回处理:stream --> <result name="down" type="stream"> <!-- 下载的文件的mime类型; 指定为二进制类型即可,支持所有文件类型;(tomcat/confg/web.xml搜索exe) --> <param name="contentType">application/octet-stream</param> <!-- 对应Action类中get属性名称!(即:action类返回流的方法!) --> <param name="inputName">fileStream</param> <!-- 指定下载的响应头; 以及下载显示的文件的文件名(中文,需要url编码后) --> <param name="contentDisposition">attachment;filename=${downFileName}</param> <!-- 每次读取文件的缓存大小 --> <param name="bufferSize">1024</param> </result> </action>
</package>
</struts>
|
下载
方式1:传统的servlet中下载!
设置下载响应头!
注意:
Action的业务方法,返回null!
方式2: strtus2提供的下载方式!
代码(补充) |
package cn.itcast.c_upload;
import java.io.File; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport;
public class DownAction extends ActionSupport{
/****************一、 下载列表显示********************/ public String list() { // 获取上传目录路径 String path = ServletActionContext.getServletContext().getRealPath("/upload"); // 目录对象 File file = new File(path); // 获取目录下的所有“文件名” String[] names = file.list(); // 保存 ActionContext.getContext().getContextMap().put("names", names); return "list"; } /****************二、 下载, 处理********************/ // 1. 封装数据 (下载的文件的文件名称) private String fileName; public void setFileName(String fileName) { // 传入的文件名(get提交,中文)! // 中文处理 try { fileName = new String(fileName.getBytes("ISO8859-1"),"UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } this.fileName = fileName; } // 2. 处理下载请求 public String down(){ return "down"; // 字符换,对应的跳转类型“stream” } // 3. 返回文件流 public InputStream getFileStream(){ // 返回文件为fileName的文件流 return ServletActionContext.getServletContext().getResourceAsStream("/upload/"+ fileName); } // 4. 下载的文件名称(中文,需要url编码) public String getDownFileName(){ // 对fileName进行URL编码! try { return URLEncoder.encode(fileName, "UTF-8"); // 确保浏览器显示的下载文件是正确的! } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } }
|