写一个拦截器的HelloWorld程序。
拦截器的使用步骤
这里我们复用StrutsDay04项目的修改客户功能,并对打开修改页面的action引用自定义的拦截器。
步骤一:创建拦截器
创建包interceptor,在该包下创建拦截器组件FirstInterceptor,并实现接口Interceptor,在拦截方法中调用action业务方法,并且在调用action前后分别输出一些内容,以模拟对action请求的拦截。代码如下:
package interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 第一个拦截器 */ public class FirstInterceptor implements Interceptor { @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocationai) throws Exception { System.out.println("FirstInterceptor拦截前..."); // 执行action业务方法 ai.invoke(); System.out.println("FirstInterceptor拦截后..."); /* * 返回值匹配对应的result,但是一旦代码中调用了 * ai.invoke时,则此返回值无效,Struts2会根据 * action的返回值匹配result。如果当前代码中没有 * 调用ai.invoke,则此返回值生效。 * */ return "error"; } }
步骤二:注册拦截器
在struts.xml中,客户package下注册拦截器组件FirstInterceptor,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!--客户配置信息 --> <package name="customer" namespace="/customer" extends="struts-default"> #cold_bold <interceptors> #cold_bold <!--注册拦截器 --> #cold_bold <interceptor name="first" #cold_bold class="interceptor.FirstInterceptor"/> #cold_bold </interceptors> <!--打开修改页面 --> <action name="toUpdateCustomer" class="action.ToUpdateCustomerAction"> <result name="success"> /WEB-INF/customer/update_customer.jsp </result> </action> </package> </struts>
步骤三:引用拦截器
在修改客户action的配置下,引用已注册的拦截器,代码如下:
<?xmlversion="1.0"encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!--客户配置信息 --> <package name="customer" namespace="/customer" extends="struts-default"> <interceptors> <!--注册拦截器 --> <interceptor name="first" class="interceptor.FirstInterceptor"/> </interceptors> <!--打开修改页面 --> <action name="toUpdateCustomer" class="action.ToUpdateCustomerAction"> #cold_bold <!--引用拦截器 --> #cold_bold <interceptor-ref name="first"/> <result name="success"> /WEB-INF/customer/update_customer.jsp </result> </action> </package> </struts>
步骤四:测试
为了便于观察拦截器与Action的执行顺序,在Action的构造方法及业务方法中,输出一些内容,代码如下:
package action; importjava.util.List; importdao.CustomerDAO; importentity.City; importentity.Customer; importentity.Sex; public class ToUpdateCustomerAction { // output private Customer customer; // 客户 private List<Sex> sexes; // 性别 private List<City> cities; // 城市 #cold_bold publicToUpdateCustomerAction() { #cold_bold System.out.println("实例化ToUpdateCustomerAction..."); #cold_bold } public String execute() { #cold_bold System.out.println("调用ToUpdateCustomerAction业务方法..."); CustomerDAOdao = new CustomerDAO(); // 模拟查询客户 customer = dao.findById(); // 模拟查询全部的性别 sexes = dao.findAllSex(); // 模拟查询全部的城市 cities = dao.findAllCities(); return "success"; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public List<Sex>getSexes() { return sexes; } public void setSexes(List<Sex> sexes) { this.sexes = sexes; } public List<City>getCities() { return cities; } public void setCities(List<City> cities) { this.cities = cities; } }
重新部署并重启tomcat,然后重新访问修改客户页面,控制台输出结果如下图:
图-1
从输出结果中可以看出,拦截器是在实例化Action之后,调用业务方法之前开始调用的。
本案例的完整代码如下所示:
FirstInterceptor完整代码
package interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 第一个拦截器 */ public class FirstInterceptor implements Interceptor { @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocationai) throws Exception { System.out.println("FirstInterceptor拦截前..."); // 执行action业务方法 ai.invoke(); System.out.println("FirstInterceptor拦截后..."); /* * 返回值匹配对应的result,但是一旦代码中调用了 * ai.invoke时,则此返回值无效,Struts2会根据 * action的返回值匹配result。如果当前代码中没有 * 调用ai.invoke,则此返回值生效。 * */ return "error"; } }
ToUpdateCustomerAction完整代码:
package action; importjava.util.List; importdao.CustomerDAO; importentity.City; importentity.Customer; importentity.Sex; public class ToUpdateCustomerAction { // output private Customer customer; // 客户 private List<Sex> sexes; // 性别 private List<City> cities; // 城市 publicToUpdateCustomerAction() { System.out.println("实例化ToUpdateCustomerAction..."); } public String execute() { System.out.println("调用ToUpdateCustomerAction业务方法..."); CustomerDAOdao = new CustomerDAO(); // 模拟查询客户 customer = dao.findById(); // 模拟查询全部的性别 sexes = dao.findAllSex(); // 模拟查询全部的城市 cities = dao.findAllCities(); return "success"; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public List<Sex>getSexes() { return sexes; } public void setSexes(List<Sex> sexes) { this.sexes = sexes; } public List<City>getCities() { return cities; } public void setCities(List<City> cities) { this.cities = cities; } }
struts.xml完整代码:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 客户配置信息 --> <package name="customer" namespace="/customer" extends="struts-default"> <interceptors> <!-- 注册拦截器 --> <interceptor name="first" class="interceptor.FirstInterceptor"/> </interceptors> <!-- 打开修改页面 --> <action name="toUpdateCustomer" class="action.ToUpdateCustomerAction"> <!-- 引用拦截器 --> <interceptor-ref name="first"/> <result name="success"> /WEB-INF/customer/update_customer.jsp </result> </action> </package> </struts>
扩展拦截器HelloWorld示例,练习使用拦截器栈,并观察多个拦截器的执行顺序。
创建一个新的拦截器,与第一个拦截器打包成栈,然后让修改客户的action引用这个拦截器栈,并观察控制台中这两个拦截器与Action的执行顺序。
实现此案例需要按照如下步骤进行。
步骤一:创建一个新的拦截器
创建一个新的拦截器组件SecondInterceptor,代码如下:
package interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 第二个拦截器 */ public class SecondInterceptor implements Interceptor { @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocationai) throws Exception { System.out.println("SecondInterceptor拦截前..."); ai.invoke(); System.out.println("SecondInterceptor拦截后..."); return "error"; } }
步骤二:注册新拦截器,并打包成栈
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!--客户配置信息 --> <package name="customer" namespace="/customer" extends="struts-default"> <interceptors> <!--注册拦截器 --> <interceptor name="first" class="interceptor.FirstInterceptor"/> #cold_bold <interceptor name="second" #cold_bold class="interceptor.SecondInterceptor"/> #cold_bold <!--注册拦截器栈 --> #cold_bold <interceptor-stack name="myStack"> #cold_bold <interceptor-ref name="first"/> #cold_bold <interceptor-ref name="second"/> #cold_bold </interceptor-stack> </interceptors> <!--打开修改页面 --> <action name="toUpdateCustomer" class="action.ToUpdateCustomerAction"> <!--引用拦截器 --> <interceptor-ref name="first"/> <result name="success"> /WEB-INF/customer/update_customer.jsp </result> </action> </package> </struts>
步骤三:引用拦截器栈
可以在action的配置下引用拦截器,也可以给一个包下所有的action统一设置默认使用的拦截器,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!--客户配置信息 --> <package name="customer" namespace="/customer" extends="struts-default"> <interceptors> <!--注册拦截器 --> <interceptor name="first" class="interceptor.FirstInterceptor"/> <interceptor name="second" class="interceptor.SecondInterceptor"/> <!--注册拦截器栈 --> <interceptor-stack name="myStack"> <interceptor-ref name="first"/> <interceptor-ref name="second"/> </interceptor-stack> </interceptors> #cold_bold <!--设置当前包下所有Action默认引用的拦截器 --> #cold_bold <default-interceptor-ref name="myStack"/> <!--打开修改页面 --> <action name="toUpdateCustomer" class="action.ToUpdateCustomerAction"> #cold_bold <!--引用拦截器 --> #cold_bold <!--<interceptor-ref name="first"/> --> <result name="success"> /WEB-INF/customer/update_customer.jsp </result> </action> </package> </struts>
步骤四:测试
重新部署项目并重启tomcat,重新访问修改客户页面时,控制台输出结果如下图:
图-2
从图中可以看出,多个拦截器拦截Action的顺序满足先进后出的原则,其顺序可以按照下图来理解和记忆:
图-3
本案例的完整代码如下。
SecondInterceptor完整代码:
package interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 第二个拦截器 */ public class SecondInterceptor implements Interceptor { @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocationai) throws Exception { System.out.println("SecondInterceptor拦截前..."); ai.invoke(); System.out.println("SecondInterceptor拦截后..."); return "error"; } }
struts.xml完整代码:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 客户配置信息 --> <package name="customer" namespace="/customer" extends="struts-default"> <interceptors> <!-- 注册拦截器 --> <interceptor name="first" class="interceptor.FirstInterceptor"/> <interceptor name="second" class="interceptor.SecondInterceptor"/> <!-- 注册拦截器栈 --> <interceptor-stack name="myStack"> <interceptor-ref name="first"/> <interceptor-ref name="second"/> </interceptor-stack> </interceptors> <!-- 设置当前包下所有Action默认引用的拦截器 --> <default-interceptor-ref name="myStack"/> <!-- 打开修改页面 --> <action name="toUpdateCustomer" class="action.ToUpdateCustomerAction"> <!-- 引用拦截器 --> <!--<interceptor-ref name="first"/> --> <result name="success"> /WEB-INF/customer/update_customer.jsp </result> </action> </package> </struts>
当前NetCTOSS项目中,我们可以直接在地址栏中输入地址访问资费模块的功能,这使得登录功能形同虚设。要求在没登录时不允许直接访问资费模块的任何action,登录后才可以访问。
需要在访问资费action时进行校验,判断是否已进行了登录。这种判断每一个action都要处理,甚至以后有更多业务模块时也要做这样的事情,因此是action通用的业务逻辑,可以使用拦截器来处理。
我们可以创建拦截器组件,在拦截方法中调用action业务方法之前,判断是否进行过登录,从而确定是否可以继续访问该action,然后给每一个业务模块注册该action即可实现此需求。
实现此案例需要按照如下步骤进行。
步骤一:创建登录检查拦截器
创建com.netctoss.interceptor包,在该包下创建登录检查拦截器LoginInterceptor,代码如下:
packagecom.netctoss.interceptor; importjava.util.Map; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 登录检查拦截器,用于检查用户是否登录。 */ public class LoginInterceptor implements Interceptor { @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocationai) throws Exception { // 获取session Map<String, Object> session = ai.getInvocationContext().getSession(); // 从session中读取登录信息 Object admin = session.get("admin"); // 如果登录信息为空,则踢回登录页,而不调用业务Action if (admin == null) { return "login"; } else { // 如果登录信息不为空,则调用业务Action returnai.invoke(); } } }
步骤二:使用登录检查拦截器
在struts.xml中注册该拦截器,然后将其与默认拦截器栈打包成新的拦截器栈loginStack,再通过default-interceptor-ref标记设置action默认使用的拦截器为loginStack。在检查到未登录时,需要找到名为login的result,跳转回登录页面,这个result就需要是所有action公用的,可以在global-results标记下定义这个result,来实现被action复用的目的。代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> #cold_bold <!--公共的包,封装了通用的拦截器、通用的result --> #cold_bold <package name="netctoss" extends="json-default"> #cold_bold <interceptors> #cold_bold <!--登录检查拦截器 --> #cold_bold <interceptor name="loginInterceptor" #cold_bold class="com.netctoss.interceptor.LoginInterceptor"/> #cold_bold <!--登录检查拦截器栈 --> #cold_bold <interceptor-stack name="loginStack"> #cold_bold <interceptor-ref name="loginInterceptor"/> #cold_bold <!--不要丢掉默认的拦截器栈,里面有很多Struts2依赖的拦截器 --> #cold_bold <interceptor-ref name="defaultStack"/> #cold_bold </interceptor-stack> #cold_bold </interceptors> #cold_bold <!--设置action默认引用的拦截器 --> #cold_bold <default-interceptor-ref name="loginStack"/> #cold_bold <!--全局的result,包下所有的action都可以共用 --> #cold_bold <global-results> #cold_bold <!--跳转到登录页面的result --> #cold_bold <result name="login" type="redirectAction"> #cold_bold <param name="namespace">/login</param> #cold_bold <param name="actionName">toLogin</param> #cold_bold </result> #cold_bold </global-results> #cold_bold </package> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> #cold_bold <package name="cost" namespace="/cost" extends="netctoss"> <!--查询资费数据 --> <action name="findCost" class="com.netctoss.action.FindCostAction"> <!-- 正常情况下跳转到资费列表页面。 一般一个模块的页面要打包在一个文件夹下,并且文件夹以模块名命名。 --> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> <!-- 错误情况下,跳转到错误页面。 错误页面可以被所有模块复用,因此放在main下, 该文件夹用于存放公用的页面。 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> <!--删除资费 --> <action name="deleteCost" class="com.netctoss.action.DeleteCostAction"> <!--删除完之后,重定向到查询action --> <result name="success" type="redirectAction"> findCost </result> <result name="error"> /WEB-INF/main/error.jsp </result> </action> <!--打开资费新增页 --> <action name="toAddCost"> <result name="success"> /WEB-INF/cost/add_cost.jsp </result> </action> <!--资费名唯一性校验 --> <action name="checkCostName" class="com.netctoss.action.CheckCostNameAction"> <!--使用json类型的result把结果输出给回调函数 --> <result name="success" type="json"> <param name="root">info</param> </result> </action> <!--打开修改页面 --> <action name="toUpdateCost" class="com.netctoss.action.ToUpdateCostAction"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> <result name="error"> /WEB-INF/main/error.jsp </result> </action> </package> <!--登录模块 --> <package name="login" namespace="/login" extends="struts-default"> <!-- 打开登录页面: 1、action的class属性可以省略,省略时Struts2 会自动实例化默认的Action类ActionSupport, 该类中有默认业务方法execute,返回success。 2、action的method属性可以省略,省略时Struts2 会自动调用execute方法。 --> <action name="toLogin"> <result name="success"> /WEB-INF/main/login.jsp </result> </action> <!--登录校验 --> <action name="login" class="com.netctoss.action.LoginAction"> <!--校验成功,跳转到系统首页 --> <result name="success"> /WEB-INF/main/index.jsp </result> <!--登录失败,跳转回登录页面 --> <result name="fail"> /WEB-INF/main/login.jsp </result> <!--报错,跳转到错误页面 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> <!--生成验证码 --> <action name="createImage" class="com.netctoss.action.CreateImageAction"> <!--使用stream类型的result --> <result name="success" type="stream"> <!--指定输出的内容 --> <param name="inputName">imageStream</param> </result> </action> </package> </struts>
注意:上述代码中将拦截器以及公共的result提出来放到了公共的包下,然后让资费的包继承此包,这样做的好处是可以复用这些公共的代码,因为将来还会有其他的业务模块,他们也需要使用这些代码。
步骤三:测试
部署项目并重启tomcat,然后直接访问资费模块的某action,会发现页面自动跳转到了登录页,这是因为拦截器的作用所致。在正常登录之后,再去输入地址访问资费模块某action时,就可以正常访问了。
本案例的完整代码如下所示:
LoginInterceptor完整代码
packagecom.netctoss.interceptor; importjava.util.Map; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 登录检查拦截器,用于检查用户是否登录。 */ public class LoginInterceptor implements Interceptor { @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocationai) throws Exception { // 获取session Map<String, Object> session = ai.getInvocationContext().getSession(); // 从session中读取登录信息 Object admin = session.get("admin"); // 如果登录信息为空,则踢回登录页,而不调用业务Action if (admin == null) { return "login"; } else { // 如果登录信息不为空,则调用业务Action returnai.invoke(); } } }
struts.xml完整代码
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 公共的包,封装了通用的拦截器、通用的result --> <package name="netctoss" extends="json-default"> <interceptors> <!-- 登录检查拦截器 --> <interceptor name="loginInterceptor" class="com.netctoss.interceptor.LoginInterceptor"/> <!-- 登录检查拦截器栈 --> <interceptor-stack name="loginStack"> <interceptor-ref name="loginInterceptor"/> <!-- 不要丢掉默认的拦截器栈,里面有很多Struts2依赖的拦截器 --> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <!-- 设置action默认引用的拦截器 --> <default-interceptor-ref name="loginStack"/> <!-- 全局的result,包下所有的action都可以共用 --> <global-results> <!-- 跳转到登录页面的result --> <result name="login" type="redirectAction"> <param name="namespace">/login</param> <param name="actionName">toLogin</param> </result> </global-results> </package> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> <package name="cost" namespace="/cost" extends="netctoss"> <!-- 查询资费数据 --> <action name="findCost" class="com.netctoss.action.FindCostAction"> <!-- 正常情况下跳转到资费列表页面。 一般一个模块的页面要打包在一个文件夹下,并且文件夹以模块名命名。 --> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> <!-- 错误情况下,跳转到错误页面。 错误页面可以被所有模块复用,因此放在main下, 该文件夹用于存放公用的页面。 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> <!-- 删除资费 --> <action name="deleteCost" class="com.netctoss.action.DeleteCostAction"> <!-- 删除完之后,重定向到查询action --> <result name="success" type="redirectAction"> findCost </result> <result name="error"> /WEB-INF/main/error.jsp </result> </action> <!-- 打开资费新增页 --> <action name="toAddCost"> <result name="success"> /WEB-INF/cost/add_cost.jsp </result> </action> <!-- 资费名唯一性校验 --> <action name="checkCostName" class="com.netctoss.action.CheckCostNameAction"> <!-- 使用json类型的result把结果输出给回调函数 --> <result name="success" type="json"> <param name="root">info</param> </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="com.netctoss.action.ToUpdateCostAction"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> <result name="error"> /WEB-INF/main/error.jsp </result> </action> </package> <!-- 登录模块 --> <package name="login" namespace="/login" extends="struts-default"> <!-- 打开登录页面: 1、action的class属性可以省略,省略时Struts2 会自动实例化默认的Action类ActionSupport, 该类中有默认业务方法execute,返回success。 2、action的method属性可以省略,省略时Struts2 会自动调用execute方法。 --> <action name="toLogin"> <result name="success"> /WEB-INF/main/login.jsp </result> </action> <!-- 登录校验 --> <action name="login" class="com.netctoss.action.LoginAction"> <!-- 校验成功,跳转到系统首页 --> <result name="success"> /WEB-INF/main/index.jsp </result> <!-- 登录失败,跳转回登录页面 --> <result name="fail"> /WEB-INF/main/login.jsp </result> <!-- 报错,跳转到错误页面 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> <!-- 生成验证码 --> <action name="createImage" class="com.netctoss.action.CreateImageAction"> <!-- 使用stream类型的result --> <result name="success" type="stream"> <!-- 指定输出的内容 --> <param name="inputName">imageStream</param> </result> </action> </package> </struts>
在Struts2框架下实现文件上传。
Struts2提供了拦截器可以自动实现文件上传,并且该拦截器存在于defaultStack中,是每个action默认使用的。
我们可以复用StrutsDay04的项目,在此基础上做文件上传示例。
实现此案例需要按照如下步骤进行。
步骤一:打开上传页面
在struts.xml中,配置打开上传页面的action,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!--客户配置信息 --> <package name="customer" namespace="/customer" extends="struts-default"> <interceptors> <!--注册拦截器 --> <interceptor name="first" class="interceptor.FirstInterceptor"/> <interceptor name="second" class="interceptor.SecondInterceptor"/> <!--注册拦截器栈 --> <interceptor-stack name="myStack"> <interceptor-ref name="first"/> <interceptor-ref name="second"/> </interceptor-stack> </interceptors> <!--设置当前包下所有Action默认引用的拦截器 --> <default-interceptor-ref name="myStack"/> <!--打开修改页面 --> <action name="toUpdateCustomer" class="action.ToUpdateCustomerAction"> <!--引用拦截器 --> <!--<interceptor-ref name="first"/> --> <result name="success"> /WEB-INF/customer/update_customer.jsp </result> </action> </package> #cold_bold <!--文件上传示例 --> #cold_bold <package name="demo" namespace="/demo" extends="struts-default"> #cold_bold <!--打开上传文件页面 --> #cold_bold <action name="toUpload"> #cold_bold <result name="success">/WEB-INF/jsp/upload.jsp</result> #cold_bold </action> #cold_bold </package> </struts>
在WEB-INF下创建文件夹jsp,并在此文件夹下创建文件上传页面upload.jsp,代码如下:
<%@page pageEncoding="utf-8"%> <html> <head></head> <body> <form action="" method="post"> <input type="file" name="some" /> <input type="submit" value="提交" /> </form> </body> </html>
重新部署并重启tomcat,访问此页面,效果如下图:
图-4
步骤二:导包
上传文件需要依赖新的包 commons-io-1.3.2.jar,将其引入到项目中后,包结构如下图:
图-5
步骤三:Action中处理上传请求
由于Action需要接收拦截器传入的临时文件,并对临时文件进行复制,因此需要提供一个文件操作的工具类FileUtil,代码如下:
packageutil; importjava.io.BufferedInputStream; importjava.io.BufferedOutputStream; importjava.io.File; importjava.io.FileInputStream; importjava.io.FileOutputStream; importjava.io.IOException; /** * 文件处理工具 */ public class FileUtil { public static boolean copy(File src, File dest) { BufferedInputStreambis = null; BufferedOutputStreambos = null; try { bis = new BufferedInputStream(new FileInputStream(src)); bos = new BufferedOutputStream(new FileOutputStream(dest)); byte[] bts = new byte[1024]; intlen = -1; while ((len = bis.read(bts)) != -1) { bos.write(bts, 0, len); } return true; } catch (Exception e) { e.printStackTrace(); return false; } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } if (bos != null) { try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
然后创建上传文件Action类UploadAction,并根据拦截器传入的临时文件,将其赋值到某路径下。代码如下:
package action; importjava.io.File; import org.apache.struts2.ServletActionContext; importutil.FileUtil; /** * 文件上传Action */ public class UploadAction { /** * 接收拦截器传入的临时文件 */ private File some; /** * 接收拦截器注入的原始文件名 */ private String someFileName; publicString execute() { if (some == null) return "error"; // 将文件放于项目部署路径下的upload文件夹下 String path = "WEB-INF/upload/" + someFileName; // 根据相对部署路径计算完整路径 path = ServletActionContext.getServletContext().getRealPath(path); // 将临时文件复制到上述路径下 FileUtil.copy(some, new File(path)); return "success"; } public File getSome() { return some; } public void setSome(File some) { this.some = some; } public String getSomeFileName() { returnsomeFileName; } public void setSomeFileName(String someFileName) { this.someFileName = someFileName; } }
上述代码中,用意是将文件复制部署的项目路径下的WEB-INF/upload文件夹下,这样就可以不在项目中写死上传文件的路径了,而是随着项目部署位置的不同而自动变化,比较灵活。因此这里要求我们在项目中的WEB-INF下创建新的文件夹upload,创建后该文件夹结构如下图:
图-6
步骤四:struts.xml中配置上传文件action
在struts.xml中配置该action,并且设置文件上传的最大值,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!--客户配置信息 --> <package name="customer" namespace="/customer" extends="struts-default"> <interceptors> <!--注册拦截器 --> <interceptor name="first" class="interceptor.FirstInterceptor"/> <interceptor name="second" class="interceptor.SecondInterceptor"/> <!--注册拦截器栈 --> <interceptor-stack name="myStack"> <interceptor-ref name="first"/> <interceptor-ref name="second"/> </interceptor-stack> </interceptors> <!--设置当前包下所有Action默认引用的拦截器 --> <default-interceptor-ref name="myStack"/> <!--打开修改页面 --> <action name="toUpdateCustomer" class="action.ToUpdateCustomerAction"> <!--引用拦截器 --> <!--<interceptor-ref name="first"/> --> <result name="success"> /WEB-INF/customer/update_customer.jsp </result> </action> </package> #cold_bold <!--设置上传文件最大值 --> #cold_bold <constant name="struts.multipart.maxSize" value="5000000"/> <!--文件上传示例 --> <package name="demo" namespace="/demo" extends="struts-default"> <!--打开上传文件页面 --> <action name="toUpload"> <result name="success">/WEB-INF/jsp/upload.jsp</result> </action> #cold_bold <!--上传文件 --> #cold_bold <action name="upload" class="action.UploadAction"> #cold_bold <result name="success">/WEB-INF/jsp/ok.jsp</result> #cold_bold <result name="error">/WEB-INF/jsp/error.jsp</result> #cold_bold </action> </package> </struts>
步骤五:JSP
上述配置中,如果上传成功则转至ok.jsp,该页面代码如下:
<html> <head></head> <body> <h1>OK!</h1> </body> </html>
如果上传失败则转至error.jsp,该页面代码如下:
<html> <head></head> <body> <h1 style="color:red">Error!</h1> </body> </html>
修改上传文件页面upload.jsp的表单属性值,代码如下:
<%@page pageEncoding="utf-8"%> <html> <head></head> <body> #cold_bold <!-- #cold_bold 上传文件对表单有2个要求: #cold_bold 1、method="post" #cold_bold 2、enctype="multipart/form-data" #cold_bold --> #cold_bold <form action="upload" method="post" enctype="multipart/form-data"> <input type="file" name="some" /> <input type="submit" value="提交" /> </form> </body> </html>
步骤六:测试
重新部署项目并重启tomcat,打开上传文件页面,选择一个文件后点提交按钮,文件被正确上传至项目部署路径下的WEB-INF/upload文件夹下了,效果如下图:
图-7
本案例的完整代码如下。
FileUtil完整代码:
packageutil; importjava.io.BufferedInputStream; importjava.io.BufferedOutputStream; importjava.io.File; importjava.io.FileInputStream; importjava.io.FileOutputStream; importjava.io.IOException; /** * 文件处理工具 */ public class FileUtil { public static boolean copy(File src, File dest) { BufferedInputStreambis = null; BufferedOutputStreambos = null; try { bis = new BufferedInputStream(new FileInputStream(src)); bos = new BufferedOutputStream(new FileOutputStream(dest)); byte[] bts = new byte[1024]; intlen = -1; while ((len = bis.read(bts)) != -1) { bos.write(bts, 0, len); } return true; } catch (Exception e) { e.printStackTrace(); return false; } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } if (bos != null) { try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
struts.xml完整代码:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 客户配置信息 --> <package name="customer" namespace="/customer" extends="struts-default"> <interceptors> <!-- 注册拦截器 --> <interceptor name="first" class="interceptor.FirstInterceptor"/> <interceptor name="second" class="interceptor.SecondInterceptor"/> <!-- 注册拦截器栈 --> <interceptor-stack name="myStack"> <interceptor-ref name="first"/> <interceptor-ref name="second"/> </interceptor-stack> </interceptors> <!-- 设置当前包下所有Action默认引用的拦截器 --> <default-interceptor-ref name="myStack"/> <!-- 打开修改页面 --> <action name="toUpdateCustomer" class="action.ToUpdateCustomerAction"> <!-- 引用拦截器 --> <!--<interceptor-ref name="first"/> --> <result name="success"> /WEB-INF/customer/update_customer.jsp </result> </action> </package> <!-- 设置上传文件最大值 --> <constant name="struts.multipart.maxSize" value="5000000"/> <!-- 文件上传示例 --> <package name="demo" namespace="/demo" extends="struts-default"> <!-- 打开上传文件页面 --> <action name="toUpload"> <result name="success">/WEB-INF/jsp/upload.jsp</result> </action> <!-- 上传文件 --> <action name="upload" class="action.UploadAction"> <result name="success">/WEB-INF/jsp/ok.jsp</result> <result name="error">/WEB-INF/jsp/error.jsp</result> </action> </package> </struts>
UploadAction完整代码:
package action; importjava.io.File; import org.apache.struts2.ServletActionContext; importutil.FileUtil; /** * 文件上传Action */ public class UploadAction { /** * 接收拦截器传入的临时文件 */ private File some; /** * 接收拦截器注入的原始文件名 */ private String someFileName; publicString execute() { if (some == null) return "error"; // 将文件放于项目部署路径下的upload文件夹下 String path = "WEB-INF/upload/" + someFileName; // 根据相对部署路径计算完整路径 path = ServletActionContext.getServletContext().getRealPath(path); // 将临时文件复制到上述路径下 FileUtil.copy(some, new File(path)); return "success"; } public File getSome() { return some; } public void setSome(File some) { this.some = some; } public String getSomeFileName() { returnsomeFileName; } public void setSomeFileName(String someFileName) { this.someFileName = someFileName; } }
upload.jsp完整代码:
<%@page pageEncoding="utf-8"%> <html> <head></head> <body> <!-- 上传文件对表单有2个要求: 1、method="post" 2、enctype="multipart/form-data" --> <form action="upload" method="post" enctype="multipart/form-data"> <input type="file" name="some" /> <input type="submit" value="提交" /> </form> </body> </html>
ok.jsp完整代码:
<html> <head></head> <body> <h1>OK!</h1> </body> </html>
error.jsp完整代码:
<html> <head></head> <body> <h1 style="color:red">Error!</h1> </body> </html>