JAVAWEB开发之Struts2详解(三)——Struts2信息国际化、自定义拦截器以及Struts2文件上传和下载详解 绝地灬酷狼 2022-09-28 05:51 130阅读 0赞 ## Struts2中的信息国际化 ## 1.国际化原理?什么是国际化? 同一款软件,可以为不同用户,提供不同的语言界面(国际化软件) 需要一个语言资源包(很多properties文件,每个properties文件 针对一个国家或一种语言,通过Java程序根据来访者国家语言,自动读取不同的properties文件) 2.资源包编写 properties文件命名:基本名称\_语言(小写)\_国家(大写).properties 例如: message\_zh\_CN.properties 中国中文 message\_en\_US.properties 美国英文 3.ResourceBundle 根据不同Locale(地域信息),读取不同国家properties文件 ResourceBundle bundle=ResourceBundle.getBundle("message",Locale.US); Struts2中的信息国际化: Struts2中对国际化进行了封装,我们只需要根据其提供的API进行访问就可以。 ## Struts2国际化配置与使用 ## ### 在Struts2中国际化时properties文件的定义方式: ### (1)全局(所有Action都可以使用) 这种方式也最常用,对于properties配置文件可以放在任何包中,需要在Struts中进行配置 需要引用Struts2 中core下的org.apache.struts2包中default.properties中的一个常量 struts.custom.i18n.resources=testmessages,testmessages2 在Struts2中配置默认国际化常量如下: <constant name="struts.custom.i18n.resources" value="message"> 代表message.properties在src下 <constant name="struts.custom.i18n.resources" value="cn.itcast.i18n.resource.message"> 代表message.properties在cn.itcast.i18n.resource包下. (2)Action范围信息文件(只能在某个Action中使用) 数据只能在对应Action中使用,配置文件位置:与Action在同一包下 名称:Action类名.properties 无需在struts.xml中进行额外配置。 (3)package范围信息文件(package中所有Action都可以使用) 数据对包(包括子包)中的所有Action都有效,在指定的包中创建package.properties (4)临时信息文件(主要在jsp中引入) <s:i18n name="cn.itcast.action.package"></s:i18n> ### 一般在哪些位置上使用Struts2国际化操作? ### * Action类中使用 * 配置文件中使用 例如:xxx-validation.xml * 在jsp页面上使用 ### 怎样在Struts2中操作国际化? ### * 第一种:在Action类中使用。前提是Action类要继承自ActionSupport类。可以通过getText(String name)就可以获取配置文件中对应名称的值。 * 第二种:在validation.xml文件中使用。例如:<message key="名称"/> * 第三种:在JSP页面上使用。<s:text name="名称"> 如果没有使用<s:i18n name="">来指定,会从全局配置文件中获取。如果要从一个配置文件中获取,通过name属性来指定包名.配置文件名 ### 在Struts2国际化配置中使用动态文本 ### (1)在Action中使用动态文本 配置文件中:msg=hello world \{0\} 在action类中 this.getText("msg", new String\[\]\{"tom"\})结果就是:hello world tom (2)JSP页面上怎样使用 配置文件中:msg=hello world \{0\} 在JSP中 <s:i18n name="cn.itcast.action.I18nDemo1Action"> <s:text name="msg"> <s:param>张三</s:param> </s:text> </s:i18n> 结果就是hello world 程序实例如下: ![Center][] ![Center 1][] I18nDemo1Action(测试action获取国际化资源内容) package cn.itcast.action; import com.opensymphony.xwork2.ActionSupport; public class I18nDemo1Action extends ActionSupport { @Override public String execute() throws Exception { // 得到properties文件中的信息 System.out.println(this.getText("msg")); // 动态文本 System.out.println(this.getText("msg", new String[] { "tom" })); return NONE; } } I18nDemo2Action(测试validation.xml中获取资源文件内容) package cn.itcast.action; import com.opensymphony.xwork2.ActionSupport; public class I18nDemo2Action extends ActionSupport { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String execute() throws Exception { return NONE; } } I18nDemo1Action.properties (action范围的资源文件 针对I18nDemo1Action) msg=hello world {0} I18nDemo2Action-validation.xml(I18nDemo2Action的配置校验文件) <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"> <validators> <field name="name"> <field-validator type="requiredstring"> <message key="nameerror"></message> </field-validator> </field> </validators> package\_en\_US.properties(指定美国英语格式 当前包内范围内使用) nameerror=name required package\_zh\_CN.properties (指定中国英文格式 当前包范围内使用) ![Center 2][] package.properties(当找不到指定的Locale指定的资源文件时 就从这个默认的资源文件中取) nameerror=name required 在cn.itcast.i18n.resource资源包下创建全局的资源文件 message.properties name=tom message\_en\_US.properties name=tom message\_zh\_CN.properties ![Center 3][] 配置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> <!-- 声明一个全局的国际化文件 --> <constant name="struts.custom.i18n.resources" value="cn.itcast.i18n.resource.message"></constant> <package name="default" namespace="/" extends="struts-default"> <action name="i18ndemo1" class="cn.itcast.action.I18nDemo1Action"></action> <action name="i18ndemo2" class="cn.itcast.action.I18nDemo2Action"> <result name="input">/input.jsp</result> </action> </package> </struts> i18n.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> </head> <body> <s:i18n name="cn.itcast.action.package"> <s:text name="nameerror"></s:text> </s:i18n> <br> <s:i18n name="cn.itcast.action.I18nDemo1Action"> <s:text name="msg"> <s:param>张三</s:param> </s:text> </s:i18n> <br> <s:text name="name" /> </body> </html> input.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> </head> <body> <s:fielderror /> </body> </html> 首先测试jsp上获取资源文件内容: ![Center 4][] 测试配置文件获取资源文件内容 ![Center 5][] 测试Action中获取资源文件内容 ![Center 6][] ## Struts2拦截器 ## Struts2拦截器在访问某个Action方法之前或之后实施拦截。 Struts2拦截器是可插拔的,拦截器是AOP(面向切面编程的一种实现) 拦截器采用责任链模式,在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成的一条链。责任链里的每一个节点都可以继续调用下一个节点,也可以阻值流程继续执行。 在Struts2中可以定义很多个拦截器,将多个拦截器按照特定顺序,组成拦截器栈(顺序调用栈中的每一个拦截器) ### Struts2执行原理—底层分析 ### ![Center 7][] Struts2的工作机制: 一个请求在Struts2框架中的处理大概分为以下几个步骤: 1、客户端初始化一个指向Servlet容器(例如Tomcat)的请求。 2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin) 3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action。注意的是:FilterDispatcher类已经过时,早已被StrutsPrepareAndExecuteFilter 4、如果ActionMapper决定需要调用某个Action,StrutsPrepareAndExecuteFilter把请求的处理交给ActionProxy。 5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类。 6、ActionProxy创建一个ActionInvocation的实例。 7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Interceptor)的调用。 8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模板。在表示的过程中可以使用Struts2框架中继承的标签。在这个过程中需要涉及到ActionMapper。 ### Interceptor接口 ### 每个拦截器都是实现了com.opensymphony.xwork2.interceptor.Interceptor接口的Java类 此接口的源代码(已删除注释)如下: public interface Interceptor extends Serializable { void destroy(); void init(); String intercept(ActionInvocation invocation) throws Exception; } init:该方法将在拦截器被创建后立即被调用,它在拦截器的生命周期内只被调用一次,可以在该方法中对相关资源进行必要的初始化。 interceptor:每拦截一个动作请求,该方法就会被调用一次。 destroy:该方法将在拦截器被销毁之前被调用,它在拦截器的生命周期内也只被调用一次。 注意: * Struts 会依次调用程序员为某个 Action 而注册的每一个拦截器的 interecept 方法. * 每次调用 interecept 方法时, Struts 会传递一个**ActionInvocation**接口的实例. * ActionInvocation: 代表一个给定动作的执行状态, 拦截器可以从该类的对象里获得与该动作相关联的 Action 对象和 Result 对象. 在完成拦截器自己的任务之后, 拦截器将调用 ActionInvocation 对象的 invoke 方法前进到 Action 处理流程的下一个环节. * 还可以调用 ActionInvocation 对象的 addPreResultListener 方法给 ActionInvocation 对象 “挂” 上一个或多个 PreResultListener 监听器. 该监听器对象可以在动作执行完毕之后, 开始执行动作结果之前做些事情 * **AbstractInterceptor**类实现了 Interceptor 接口. 并为 init, destroy 提供了一个空白的实现 Struts2自带的拦截器 * Alias Interceptor 名字:alias说明:在不同请求之间将参数在不同名字间转换,请求内容不变。 * Chaining Interceptor 名字:chain 说明:让前一个Action的属性可以被后一个Action访问,现在和chain类型的result()结合使用。 * Checkbox Interceptor 名字:checkbox说明:添加了checkbox自动处理代码,将没有选中checkbox的内容设置为false,而HTML默认情况下不提交没有选中的checkbox。 * Cookies Interceptor 名字:cookies说明:使用配置的name,value是指cookies。 * Conversion Error Interceptor 名字:conversionError说明:将错误从ActionContext中添加到Action的属性字段中。 * Create Session Interceptor 名字:createSession 说明:自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。 * Debugging Interceptor 名字:debugging说明:提供不同的调试用的页面来展现内部的数据状况。 * Execute and wait Interceptor 名字:execAndWait说明:在后台执行Action,同时将用户带到一个中间的等待页面。 * Exception Interceptor 名字:exception说明:将异常定位到一个画面。 * File Upload Interceptor 名字:fileUpload说明:提供文件上传功能。 * I18n Interceptor 名字:i18n说明:记录用户选择的locale。 * Logger Interceptor 名字:logger说明:输出Action的名字。 * Message Store Interceptor 名字:store说明:存储或者访问实现ValidationAware接口的Action出现的消息,错误,字段错误等。 * Model DrivenInterceptor 名字:model-driven说明:如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。 * Scoped Model Driven 名字:scoped-model-driven 说明:如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model 调用Action的setModel方法将其放入Action内部。 * Parameters Interceptor 名字:params说明:将请求中的参数设置到Action中。 * Prepare Interceptor 名字:prepare说明:如果Action实现了Preparable,则该拦截器调用Action类的prepare方法。 * Scope Interceptor 名字:scope 说明:将Action状态存入session和application的简单方法。 * Servlet Config Interceptor名字:servletConfig说明:提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问。 * Static Parameters Interceptor 名字:staticParams说明:从struts.xml文件中将中的中的内容设置到对应的Action中。 * Roles Interceptor 名字:roles 说明:确定用户是否具有JAAS指定的Role,否则不予执行。 * Timer Interceptor 名字:timer 说明:输出Action执行的时间 * Token Interceptor 名字:token 说明:通过Token来避免双击 * Token Session Interceptor 名字:tokenSession 说明:和Token Interceptor一样,不过双击的时候把请求的数据存储在Session中 * Validation Interceptor 名字:validation 说明:使用action-validation.xml文件中定义的内容校验提交的数据。 * Workflow Interceptor 名字:workflow 说明:调用Action的validate方法,一旦有错误返回,重新定位到INPUT画面 * Parameter Filter Interceptor 名字:N/A 说明:从参数列表中删除不必要的参数 * Profiling Interceptorprofiling 名字:profiling说明:通过参数激活profile ### 自定义拦截器 ### 使用拦截器的用途:可以通过使用拦截器进行控制action的访问。例如权限操作。 自定义拦截器的步骤: 步骤一:.创建一个Interceptor 可以自定义一个类实现com.opensymphony.xwork2.interceptor.Interceptor 在这个接口中有三个方法 init destory intercept, intercept方法是真正拦截的方法。 在intercept方法中如果要向下继续执行,通过其参数ActionInvocation调用它的invoke()方法就可以。如下图: public class PermissionInterceptor implements Interceptor { private static final long serialVersionUID = -5178310397732210602L; public void destroy() { } public void init() { } public String intercept(ActionInvocation invocation) throws Exception { System.out.println("进入拦截器"); if(session里存在用户){ String result = invocation.invoke(); }else{ return “logon”; } //System.out.println("返回值:"+ result); //return result; } } 步骤二:.声明一个Interceptor 在struts-default.xml文件中 <interceptors> <interceptor name="" class=""/> </interceptors> 注意:我们要自己声明一个interceptor可以在struts.xml文件中声明。 步骤三:.在action中指定使用哪些拦截器. <interceptor-ref name="my"/> 注意:只要显示声明使用了一个拦截器。那么默认的拦截器就不在加载。为了继续不影响加载原来默认的可以这样配置struts.xml <package name="itcast" namespace="/test" extends="struts-default"> <interceptors> <interceptor name=“permission" class="cn.itcast.aop.PermissionInterceptor" /> <interceptor-stack name="permissionStack"> <interceptor-ref name="defaultStack" /> <interceptor-ref name=" permission " /> </interceptor-stack> </interceptors> <action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}"> <result name="success">/WEB-INF/page/hello.jsp</result> <interceptor-ref name="permissionStack"/> </action> </package> 因为Struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用Struts2框架提供的众多功能。 如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-ref name="permissionStack"/>把拦截器定义为默认拦截器。注意:每个包只能指定一个默认拦截器。另外,一旦我们为该包中的某个action显式指定的了某个默认拦截器,则默认拦截器不会起作用。 如果对于一个Action类中有多个请求处理方法,要求对有的请求处理进行权限验证,有的不需要登录(验证权限) 则不用直接实现Interceptor接口,而是可以让Action类继承其实现类AbstractInterceptor的子类MethodFilterInterceptor ![Center 8][] 它有以下属性 ![Center 9][] 有如下方法: ![Center 10][] includeMethods 表示进行拦截的方法 excludeMethods 表示不进行拦截的方法 一般在struts.xml中配置,并且一般只写一种 例如只配置includeMethods 时 除了包含的方法外别的方法都不会再进行拦截如果只配置excludeMethods 则除了不拦截的方法外,别的方法都拦截。 例如配置方法示例如下: <interceptors> <intercept name="" class=""> <param name="includeMethods">add,update,delete</param> <param name="excludeMethods">search</param> </intercept> </interceptors> 而且Action继承这个MethodFilterInterceptor类,必须重写doInterceptor抽象方法 ### 自定义拦截器示例一:Action实现Interceptor接口 ### 创建一个Action对象 Demo1Action package cn.itcast.action; import com.opensymphony.xwork2.ActionSupport; public class Demo1Action extends ActionSupport { @Override public String execute() throws Exception { System.out.println("Demo1Action ......"); return SUCCESS; } } 创建两个拦截器分别放行和拦截后跳转 MyInterceptor package cn.itcast.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class MyInterceptor implements Interceptor { @Override public void destroy() { } @Override public void init() { System.out.println("my interceptor init"); } @Override public String intercept(ActionInvocation action) throws Exception { System.out.println("my interceptor 拦截......"); // 放行 return action.invoke(); } } MyInterceptor2 package cn.itcast.interceptor; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class MyInterceptor2 implements Interceptor { @Override public void destroy() { } @Override public void init() { System.out.println("my interceptor init"); } @Override public String intercept(ActionInvocation action) throws Exception { System.out.println("my interceptor2 拦截......"); // 放行 // return action.invoke(); return Action.LOGIN; } } 在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> <package name="default" namespace="/" extends="struts-default"> <interceptors> <interceptor name="interceptor1" class="cn.itcast.interceptor.MyInterceptor" /> <interceptor name="interceptor2" class="cn.itcast.interceptor.MyInterceptor2" /> <interceptor-stack name="customStack"> <interceptor-ref name="interceptor2" /> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <default-interceptor-ref name="interceptor1"/> <action name="demo1" class="cn.itcast.action.Demo1Action"> <result>/success.jsp</result> <result name="login">/input.jsp</result> </action> <action name="demo2" class="cn.itcast.action.Demo1Action"> <result>/success.jsp</result> <result name="login">/input.jsp</result> <interceptor-ref name="customStack" /> </action> </package> </struts> input.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> </head> <body> <h1>被拦截</h1> </body> </html> success.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> </head> <body> <h1>成功访问</h1> </body> </html> 部署到服务器 并测试结果如下: ![Center 11][] 访问/demo1如下所示: ![Center 12][] 访问/demo2.... ![Center 13][] ### 自定义拦截器示例二—继承MethodFilterInterceptor 实现权限控制 ### 1.login.jsp------>LoginAction------------->book.jsp 登录成功,将用户存储到session。 2.在book.jsp中提供crud链接。 每一个连接访问一个BookAction中一个方法。 要求:对于BookAction中的add,update,delete方法要求用户必须登录后才可以访问。search无要求。 怎样解决只控制action中某些方法的拦截? 首先,创建类不在实现Interceptor接口,而是继承其下的一个子类.MethodFilterInterceptor,不用在重写intercept方法,而是重写 doIntercept方法。 其次.在struts.xml文件中声明 <interceptors> <intercept name="" class=""> <param name="includeMethods">add,update,delete</param> <param name="excludeMethods">search</param> </intercept> </interceptors> 新建Demo 示例代码如下: ![Center 14][]![Center 15][] User package cn.itcast.domain; public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } 新建登录的Action类 LoginAction package cn.itcast.action; import org.apache.struts2.ServletActionContext; import cn.itcast.domain.User; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class LoginAction extends ActionSupport implements ModelDriven<User> { private User user = new User(); @Override public User getModel() { return user; } @Override public String execute() throws Exception { if ("tom".equals(user.getUsername()) && "123".equals(user.getPassword())) { ServletActionContext.getRequest().getSession() .setAttribute("user", user); return SUCCESS; } else { this.addActionError("用户名或密码错误"); return INPUT; } } } 新建BookAction package cn.itcast.action; import com.opensymphony.xwork2.ActionSupport; public class BookAction extends ActionSupport { public String add() throws Exception { System.out.println("book action add"); return null; } public String update() throws Exception { System.out.println("book action update"); return null; } public String delete() throws Exception { System.out.println("book action delete"); return null; } public String search() throws Exception { System.out.println("book action search"); return null; } } 新建自定义拦截器类MethodFilterInterceptor package cn.itcast.interceptor; import org.apache.struts2.ServletActionContext; import cn.itcast.action.BookAction; import cn.itcast.domain.User; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; public class BookInterceptor extends MethodFilterInterceptor { @Override protected String doIntercept(ActionInvocation invocation) throws Exception { // 1.得到session中的user User user = (User) ServletActionContext.getRequest().getSession() .getAttribute("user"); if (user == null) { Object obj = invocation.getAction(); // 得到当前拦截器对象 if (obj instanceof BookAction) { BookAction action = (BookAction) obj; action.addActionError("权限不足,请先登录"); // 存储错误信息 return Action.LOGIN; } else { return invocation.invoke(); } } return invocation.invoke(); } } 配置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> <package name="default" namespace="/" extends="struts-default"> <interceptors> <interceptor name="bookInterceptor" class="cn.itcast.interceptor.BookInterceptor"> <param name="includeMethods">add,update,delete</param> </interceptor> <interceptor-stack name="myStack"> <interceptor-ref name="bookInterceptor" /> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <action name="login" class="cn.itcast.action.LoginAction"> <result name="input">/login.jsp</result> <result>/book.jsp</result> </action> <action name="book_*" class="cn.itcast.action.BookAction" method="{1}"> <result name="login">/login.jsp</result> <interceptor-ref name="myStack" /> </action> </package> </struts> login.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> </head> <body> <s:fielderror /> <s:actionerror /> <form action="${pageContext.request.contextPath}/login" method="POST"> username:<input type="text" name="username"><br> password:<input type="password" name="password"><br> <input type="submit" value="login"> </form> </body> </html> book.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> </head> <body> <a href="${pageContext.request.contextPath}/book_add">book add</a><br> <a href="${pageContext.request.contextPath}/book_update">book update</a><br> <a href="${pageContext.request.contextPath}/book_delete">book delete</a><br> <a href="${pageContext.request.contextPath}/book_search">book search</a><br> </body> </html> 运行结果如下: ![Center 16][]![Center 17][] ![Center 18][]![Center 19][] 删除浏览器记录后再次访问 ![Center 20][] ### 剖析拦截器的执行过程: ### 源代码执行流程: 1、在StrutsPrepareAndExecuteFilter中查找在doFilter方法内有一句话 execute.executeAction (request, response, mapping) 执行Action操作 2、在executeAction执行过程中会访问Dispatcher类中的serviceAction,在这个方法中会创建一个 ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false); 这就是我们的Action的代理对象。 3、查看ActionInvocation,查看其实现类DefaultActionInvocation,在其invoke方法中有如下一段代码: if (interceptors.hasNext()) {//判断是否有下一个拦截器. final InterceptorMapping interceptor = interceptors.next(); //得到一个拦截器 String interceptorMsg = "interceptor: " + interceptor.getName(); UtilTimerStack.push(interceptorMsg); try { resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); //调用得到的拦截器的拦截方法.将本类对象传递到了拦截器中。 } finally { UtilTimerStack.pop(interceptorMsg); } } 通过源代码分析,发现在DefaultActionInvocation中就是通过递归完成所有的拦截调用操作.如下图所示 ![Center 21][] #### Interceptor与Filter的区别: #### 1、拦截器是基于Java反射机制的,而过滤器是基于函数回调的 2、过滤器依赖于Servlet容器,而拦截器不依赖于Servlet容器 3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有的请求起作用。 4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能 5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次 # Struts2中文件上传与下载 # ## 文件上传 ## 文件上传概述 * 要想使用HTML表单上传一个或多个文件,必须把HTML表单的encType属性设置为multipart/form-data, 把它的method属性设置为post * 为了让用户能够选择一个文件进行上传,程序员必须提供一个<input type="file">字段 * 关于服务器端使用Commons-fileupload组件(核心类:DiskFileItemFactory、ServletFileUpload、FileItem) ### Struts2中文件上传 ### 默认情况下Struts2框架使用的就是commons-fileupload Struts2它使用了一个interceptor帮助我们完成文件上传操作。 <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/> 步骤: 1.在JSP页面的文件上传表单里使用file标签。如果一次上传多个文件就必须使用多个标签,**但是它们的名字是相同的**。 2.在Action中新添加3个和文件上传有关的属性这三个属性的名字必须是以下格式 (关于属性设置示例 可以查看FileUploadInterceptor拦截器类中的文档注释) private File **uploadImage**; //上传的文件名(与表单中上传组件的名称一致) private String **uploadImageContentType**; // 上传文件的类型 private String **uploadImageFileName**; //上传文件的名称 注意: uploadImage是JSP页面上的file标签的名字 上传文件:<input type="file" name=" uploadImage "> 如果是上传单个组件,uploadImage属性的类型就是java.io.File,它代表被上传的文件,第二个属性和第三个属性的类型是String,它们分别代表上传文件的文件名和文件类型。定义的方式分别是: * JSP页面file组件的名称+**ContentType**, * JSP页面file组件的名称+**FileName** 如果上传多个文件,可以使用数组或List 在Action的execute的方法中使用common-io包下的FileUtils完成文件复制 单文件上传代码如下: **第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar。这两个文件可以从http://commons.apache.org/下载** **第二步:把form表的encType设置为: "multipart/form-data",如下:** <form action="$\{pageContext.request.contextPath\}/upload/uploadAction\_saveFile.action" name="form1" method="post" enctype="multipart/form-data" > 上传文件名称:<input type="file" name="uploadImage"> <input type="submit" value="上传"> </form> **第三步:在Action类中添加以下属性。属性红色部分对应于表单中文件字段的名称:** public class UploadAction\{ private File uploadImage;//得到上传的文件 private String uploadImageContentType;//得到文件的类型 private String uploadImageFileName;//得到文件的名称 //这里略省了属性的getter/setter方法 public String saveFile() throws Exception\{ ServletContext sc = ServletActionContext.getServletContext(); String realpath = sc.getRealPath("/uploadfile"); try \{ File destFile = new File(realpath, uploadImageFileName); FileUtils.copyFile(uploadImage, destFile); \} catch (IOException e) \{ e.printStackTrace(); \} return "success"; \} \} 最后定义上传出错要转向的页面 **第四步:在struts.xml文件中增加如下配置** <package name="upload" namespace="/upload" extends="struts-default" > <!-- 单文件上传 --> <action name="uploadAction\_\*" class="cn.itcast.upload.UploadAction" method="\{1\}"> <result name="success">/upload/success.jsp</result> <!-- 定义上传出错要转向的页面 --> <result name="input">/upload/error.jsp</result> </action> <package> ### Struts2中文件上传的细节: ### 1、关于控制文件上传大小 在struts-core-2.3.3.jar包下org\\apache\\struts2\\default.properties 中定义了文件上传大小 struts.multipart.maxSize=2097152 上传文件默认的大小2M, 可以在struts.xml中重新设置大小 设置方法:在struts.xml文件加载该资源文件 <!-- 设置comonFileupload上传组件允许的文件大小 才能测试出上传文件过大出现的错误信息 默认值是2M--> <constant name="struts.multipart.maxSize" value="86170804"/> 2、在Struts2中默认使用的是Commons-fileupload进行文件上传 \# struts.multipart.parser=cos \# struts.multipart.parser=pell struts.multipart.parser=jakarta 如果使用pell,cos进行文件上传,必须导入其jar包 3、如果出现问题,需要配置input视图,在页面上可以通过<s: fielderror>展示错误信息, 即在struts.xml文件中根据<result name=“input”>/upload/error.jsp</result>中所指向的error.jsp页面可以使用<s:fielderror/>显示错误信息 查看错误信息的资源文件为struts-message.properties文件(与default.properties文件在同一目录下) struts-messages.properties 文件里预定义 上传错误信息,通过覆盖对应key 显示中文信息 struts.messages.error.uploading=Error uploading: {0} struts.messages.error.file.too.large=The file is to large to be uploaded: {0} "{1}" "{2}" {3} struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3} struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3} \{0\}:<input type="file" name="uploadImage"> 中name属性的值 \{1\}:上传文件的真实名称 \{2\}:上传文件保存到临时目录的名称 \{3\}:上传文件的类型(对struts.messages.error.file.too.large是上传文件的大小) 关于这些可能出现的错误信息,在FileUploadInterceptor拦截器中的acceptFile方法中进行的拦截获取 这个方法的源代码如下所示: protected boolean acceptFile(Object action, File file, String filename, String contentType, String inputName, ValidationAware validation) { boolean fileIsAcceptable = false; // If it's null the upload failed if (file == null) { String errMsg = getTextMessage(action, "struts.messages.error.uploading", new String[]{inputName}); if (validation != null) { validation.addFieldError(inputName, errMsg); } if (LOG.isWarnEnabled()) { LOG.warn(errMsg); } } else if (maximumSize != null && maximumSize < file.length()) { String errMsg = getTextMessage(action, "struts.messages.error.file.too.large", new String[]{inputName, filename, file.getName(), "" + file.length()}); if (validation != null) { validation.addFieldError(inputName, errMsg); } if (LOG.isWarnEnabled()) { LOG.warn(errMsg); } } else if ((!allowedTypesSet.isEmpty()) && (!containsItem(allowedTypesSet, contentType))) { String errMsg = getTextMessage(action, "struts.messages.error.content.type.not.allowed", new String[]{inputName, filename, file.getName(), contentType}); if (validation != null) { validation.addFieldError(inputName, errMsg); } if (LOG.isWarnEnabled()) { LOG.warn(errMsg); } } else if ((!allowedExtensionsSet.isEmpty()) && (!hasAllowedExtension(allowedExtensionsSet, filename))) { String errMsg = getTextMessage(action, "struts.messages.error.file.extension.not.allowed", new String[]{inputName, filename, file.getName(), contentType}); if (validation != null) { validation.addFieldError(inputName, errMsg); } if (LOG.isWarnEnabled()) { LOG.warn(errMsg); } } else { fileIsAcceptable = true; } return fileIsAcceptable; } 但是发现默认的错误信息提示是英文的,要想展示中文的,实现国际化 步骤如下: 第一步:创建新的资源文件 例如fileuploadmessage.properties, 放在src下,在该资源文件中增加如下信息 struts.messages.error.uploading=上传错误: \{0\} struts.messages.error.file.too.large=上传文件太大: \{0\} "\{1\}" "\{2\}" \{3\} struts.messages.error.content.type.not.allowed=上传文件的类型不允许: \{0\} "\{1\}" "\{2\}" \{3\} struts.messages.error.file.extension.not.allowed=上传文件的后缀名不允许: \{0\} "\{1\}" "\{2\}" \{3\} 第二步:在struts.xml文件中加载该资源文件 <!-- 配置上传文件的出错信息的资源文件 --> <constant name="struts.custom.i18n.resources" value=“cn….xxx.fileuploadmessage“/> 4、关于多文件上传时的每个文件大小控制以及上传文件类型控制 对于多文件上传,在服务器端只需要将action属性声明为List集合或数组即可,在JSP页面上的每个上传组件的名称仍然是action中File数组或集合的属性值。例如: private List<File> upload; private List<String> uploadContentType; private List<String> uploadFileName; 至于控制每一个上传文件的大小以及上传文件的类型,可以配置FileUploadInterceptor拦截器中的参数,通过其属性进行控制。FileUpload拦截器负责处理文件的上传操作,它是默认的defaultStack拦截器栈中的一员,它有三个属性可以设置 * maximumSize: 上传文件的最大长度(以字节为单位), 默认值为 2 MB * allowedTypes: 允许上传文件的类型, 各类型之间以逗号分隔 * allowedExtensions: 允许上传文件扩展名, 各扩展名之间以逗号分隔 可以在 struts.xml 文件中覆盖这 3 个属性,如下图所示: ![Center 22][] 因为父包struts-default中即struts-default.xml中 文件上传拦截器的name名称被命名为fileUpload 所以在配置拦截器参数param时 为fileUpload.xxx属性 表示父包内 名称为fileUpload代表的文件上传拦截器的xxx属性 利用了ognl技术。 多文件上传的关键代码如下: JSP: <form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post"> <input type="file" name="uploadImages"> <input type="file" name="uploadImages"> </form> Action: public class uploadAction{ private File[] uploadImages;//得到上传的文件 private String[] uploadImagesContentType;//得到文件的类型 private String[] uploadImagesFileName;//得到文件的名称 //这里略省了属性的getter/setter方法 public String saveFiles() throws Exception{ ServletContext sc = ServletActionContext.getServletContext(); String realpath = sc.getRealPath("/uploadfile"); try { if(uploadImages!=null&&uploadImages.length>0){ for(int i=0;i<uploadImages.length;i++){ File destFile = new File(realpath,uploadImageFileNames[i]); FileUtils.copyFile(uploadImages[i], destFile); } } } catch (IOException e) { e.printStackTrace();}return "success"; } } ## Struts2中的文件下载 ## 平常文件下载概述: 1、超链接 2、服务器编码,通过流向客户端写回。 * 设置mimeType,通过response设置response.setContentType(String mimetype); * 设置附件下载,通过response设置 response.setHeader("Content-disposition;filename=xxx"); * 通过response获取流,将要下载的信息写出 Struts2中文件下载: 通过<result type="stream">完成 在struts-default.xml中有result-type结果集视图,在结果集视图中有 <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/> StreamResult类中有三个属性: protected String contentType = "text/plain"; //用于设置下载文件的mimeType类型 protected String contentDisposition = "inline";//用于设置进行下载操作以及下载文件的名称 protected InputStream inputStream; //用于读取要下载的文件。 public InputStream getInputStream() throws FileNotFoundException \{ FileInputStream fis = new FileInputStream("d:/upload/" + filename); return fis; \} <result type="stream"> <param name="contentType">text/plain</param> <param name="contentDisposition">attachment;filename=a.txt</param> <param name="inputStream">$\{inputStream\}</param> 会调用当前action中的getInputStream方法。 </result> 问题1:<a href="$\{pageContext.request.contextPath\}/download?filename=捕获.png">捕获.png</a>下载报错 原因:超连接是get请求,并且下载的文件是中文名称,乱码。 问题2:下载捕获文件时,文件名称就是a.txt,下载文件后缀名是png,而我们在配置文件中规定就是txt? <result type="stream"> <param name="contentType">$\{contentType\}</param> <!-- 调用当前action中的getContentType()方法 --> <param name="contentDisposition">attachment;filename=$\{downloadFileName\}</param> <param name="inputStream">$\{inputStream\}</param><!-- 调用当前action中的getInputStream()方法 --> </result> 在struts2中进行下载时,如果使用<result type="stream">它有缺陷,例如:下载点击后,取消下载,服务器端会产生异常。在开发中,解决方案:可以下载一个struts2下载操作的插件,它解决了stream问题。 关键代码(简单示例)如下: ![Center 23][] ![Center 24][] ## 文件上传与下载程序示例 ## 项目结构如下: ![Center 25][]![Center 26][] UploadAction package cn.itcast.action; import java.io.File; import java.util.List; import org.apache.commons.io.FileUtils; import com.opensymphony.xwork2.ActionSupport; public class UploadAction extends ActionSupport { // 在action类中需要声明三个属性 private List<File> upload; private List<String> uploadContentType; private List<String> uploadFileName; public List<File> getUpload() { return upload; } public void setUpload(List<File> upload) { this.upload = upload; } public List<String> getUploadContentType() { return uploadContentType; } public void setUploadContentType(List<String> uploadContentType) { this.uploadContentType = uploadContentType; } public List<String> getUploadFileName() { return uploadFileName; } public void setUploadFileName(List<String> uploadFileName) { this.uploadFileName = uploadFileName; } @Override public String execute() throws Exception { for (int i = 0; i < upload.size(); i++) { System.out.println("上传文件的类型:" + uploadContentType.get(i)); System.out.println("上传文件的名称:" + uploadFileName.get(i)); FileUtils.copyFile(upload.get(i), new File("d:/upload", uploadFileName.get(i))); } return null; } } DownloadAction package cn.itcast.action; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import org.apache.struts2.ServletActionContext; import cn.itcast.utils.DownloadUtils; import com.opensymphony.xwork2.ActionSupport; public class DownloadAction extends ActionSupport { private String filename; // 要下载文件的名称 public String getFilename() { return filename; } public void setFilename(String filename) { this.filename = filename; } // 设置下载文件mimeType类型 public String getContentType() { String mimeType = ServletActionContext.getServletContext().getMimeType( filename); return mimeType; } // 获取下载文件名称 public String getDownloadFileName() throws UnsupportedEncodingException { return DownloadUtils.getDownloadFileName(ServletActionContext .getRequest().getHeader("user-agent"), filename); } public InputStream getInputStream() throws UnsupportedEncodingException, FileNotFoundException { filename = new String(filename.getBytes("iso8859-1"), "utf-8"); // 解决中文名乱码 FileInputStream fis = new FileInputStream("d:/upload/" + filename); return fis; } @Override public String execute() throws Exception { System.out.println("进行下载......"); return super.execute(); } } 配置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> <constant name="struts.custom.i18n.resources" value="message"></constant> <package name="default" namespace="/" extends="struts-default"> <action name="upload" class="cn.itcast.action.UploadAction"> <result name="input">/upload.jsp</result> <interceptor-ref name="defaultStack"> <param name="fileUpload.maximumSize">2097152</param> <param name="fileUpload.allowedExtensions">txt,mp3,docx</param> </interceptor-ref> </action> <action name="download" class="cn.itcast.action.DownloadAction"> <result type="stream"> <param name="contentType">${contentType}</param> <!-- 调用当前action中的getContentType()方法 --> <param name="contentDisposition">attachment;filename=${downloadFileName}</param><!-- 会调用Action中getFileName方法 --> <param name="inputStream">${inputStream}</param> <!-- 调用当前action中的getInputStream()方法 --> </result> </action> </package> </struts> message.properties ![Center 27][] upload.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> </head> <body> <s:fielderror /> <form action="${pageContext.request.contextPath}/upload" method="POST" enctype="multipart/form-data"> <input type="file" name="upload"><br> <input type="file" name="upload"><br> <input type="file" name="upload"><br> <input type="submit" value="上传"> </form> </body> </html> download.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> </head> <body> <a href="${pageContext.request.contextPath}/download?filename=a.txt">a.txt</a><br> <a href="${pageContext.request.contextPath}/download?filename=测试下载.docx">测试下载.docx</a> </body> </html> ![Center 28][] ![Center 29][] ![Center 30][] ![Center 31][] ![Center 32][] [Center]: https://img-blog.csdn.net/20170310130354526?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzA4NzUxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center [Center 1]: /images/20220708/2abdae2d8b674462988d1babadc15dcd.png [Center 2]: /images/20220708/b4e5914e5aa64de2a4d265d7a6efb5bd.png [Center 3]: /images/20220708/15e011649e494ec6b6c337c96e85bca9.png [Center 4]: /images/20220708/c070c46973254dd39fb5e6a497d3da70.png [Center 5]: /images/20220708/b8f8372e955149d0af6ebd8676e6fdc2.png [Center 6]: /images/20220708/0378ac40315d4b268807eaf79041c2b2.png [Center 7]: /images/20220708/a6a3c7aafa2f4a95a31d8371f55461ef.png [Center 8]: /images/20220708/34de8d199bd54e5fb5eed1250fe830ed.png [Center 9]: /images/20220708/2796be87e6ad45489dbbeb13a344d8eb.png [Center 10]: /images/20220708/766dbc44d5474a5c82505a4220b5c659.png [Center 11]: /images/20220708/ee405506fedb4f34adbd8d5ad3d8e979.png [Center 12]: /images/20220708/41c6d36727b94d1bba2c5bfc98915204.png [Center 13]: /images/20220708/93531914bf5f47e299477171ff6df337.png [Center 14]: https://img-blog.csdn.net/20170311213518843?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzA4NzUxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center [Center 15]: /images/20220708/4a09ec6d7eb64bc995007ff07581bc71.png [Center 16]: https://img-blog.csdn.net/20170311214708784?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzA4NzUxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center [Center 17]: /images/20220708/1f5fb15117864c14a5f4a2dc396217bd.png [Center 18]: https://img-blog.csdn.net/20170311214735787?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzA4NzUxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center [Center 19]: /images/20220708/4031768c7a61420981c5ab480a055b5e.png [Center 20]: /images/20220708/fe2fd35b300c4595bf3d2be256f9a1b0.png [Center 21]: /images/20220708/7ba328e8cfcf466b9592b21256ebd8f8.png [Center 22]: /images/20220708/fd9a4d16f344415aa978ab959d370f30.png [Center 23]: /images/20220708/b0d2184f407a4ed1a38519bb6e669541.png [Center 24]: /images/20220708/3841230d85d846909ebc47ab3bade6ae.png [Center 25]: https://img-blog.csdn.net/20170312172235671?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzA4NzUxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center [Center 26]: /images/20220708/2c08cfd9e8ef4a2badec43f89dac84b7.png [Center 27]: /images/20220708/1a72aa459a184fcc9422ef65819ae8f1.png [Center 28]: https://img-blog.csdn.net/20170312172946753?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzA4NzUxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center [Center 29]: /images/20220708/2ff43d4ab3a9439d8eb0ff8c0c953cc1.png [Center 30]: /images/20220708/2fe645c193874f18b536513d41c33ec1.png [Center 31]: /images/20220708/c9ebf22689da429daed0c059264e27a3.png [Center 32]: /images/20220708/2fb8e4798a3341a2a4d05f47b2845f26.png
还没有评论,来说两句吧...