六、Struts2

Servlet做控制器存在的缺陷
  • 接收请求参数需要书写大量的request.getParameter("");
  • 如果需要的数据类型不是字符串,需要开发人员手动转化
  • 业务方法参数需要的是一个Java对象时,需要手动将零散的数据封装成对象
  • 如果需要向request作用域存数据,需要书写request.setAttribute("");
  • 跳转的视图固定在程序中(硬编码),不利于未来项目的维护

Struts2

框架概述
  • 解决项目开发中通用问题的技术,对现有项目开发中一些普遍存在的开发效率的问题进行优化,也会对线程安全性问题进行处理
  • 开发人员需要按照框架的规范加入一些逻辑完成软件的开发
  • 使用框架可以提升开发效率
  • SSH:Struts2 + Spring + Hibernate,逐渐被淘汰
  • SSM:SpringMVC + Spring + Mybatis 现在的主流
Struts2框架
  • 针对于Web应用开发中控制器层的问题,是一个典型的基于MVC设计思想的框架产品
  • Struts2框架集成了Struts1和webwork框架,本质上Struts2更多的使用了webwork框架的设计理念和开发方式,2013年左右Struts1出现安全漏洞,所以出现Struts2
  • Struts2配置文件
struts2.xml
<action name="url-pattern" class="servlet-class"> //url-pattern没有斜杠
	<result name="返回值">/返回值.jsp</result> //跳转到哪个文件
</action>
  • 获取Struts2:可以到官网下载,Struts2是apache开源组织开发的一款开源框架

  • 解压包文件:

    • apps:struts2案例项目
    • docs:struts2学习使用文档
    • lib:struts2的功能 jar包
    • src:struts2源码
  • jar:java application resource

  • war:web application resource

  • 使用Struts开发

    • 搭建开发环境

      • 引入jar包:struts2-core-2.3.16.jar和xwork-core-2.3.16.jar为struts2框架的核心jar包
      • 引入配置文件:
        • struts.xml核心配置文件必须放在根目录src下
        • src下引入log4j.properties日志管理文件,查看Struts2的运行过程
      • 初始化配置:编写web.xml文件,配置一个Struts2的核心过滤器开启Struts2框架的功能
      <!-- strtus2核心入口过滤器 -->
        <filter>
        	<filter-name>struts</filter-name>
        	<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
        </filter>
        <filter-mapping>
        	<filter-name>struts</filter-name>
          <!-- 过滤范围是所有,代表所有来自客户端的请求先进入过滤器,才能使用Struts2框架功能-->
        	<url-pattern>/*</url-pattern>
        </filter-mapping>
      
      • 配置struts.xml
      <!-- name属性全局唯一即可
      extends:struts-default 使用Struts2框架的功能
      namespace:是未来请求action路径的一部分,必须以 / 开头,且多个package不冲突 -->
      <package name="p1" extends="struts-default" namespace="/p1">
      	  <!-- name属性值为请求action路径的一部分,唯一
      	  class属性值指定action实现类的全限定名
      	  访问当前action路径:http://localhost/项目名/p1/helloWorld -->
      	  <action name="helloWorld" class="com.hang.action.HelloAction">
      	  		<result name="success">/success.jsp</result>
      	  </action>
      </package>
      
Struts2中的跳转控制
  • action —> jsp
    • 请求转发:type=“dispatcher” 默认值,代表请求转发的方式跳转到jsp
    • 请求重定向:type=“redirect” 代表请求重定向方式跳转到jsp
<action name="jump1" class="com.hang.action.JumpAction1">
	  	<!-- 通过type属性值控制跳转方式
	  	type="dispatcher"  默认值,代表请求转发的方式跳转到jsp
	  	type="redirect"  代表请求重定向方式跳转到jsp-->
	  	<result name="success" type="redirect">/success.jsp</result>
</action>
  • action —> action
    • 请求转发:type=“chain” 代表请求转发跳转到action
    • 请求重定向:type=“redirectAction” 代表请求重定向到action
      • 在struts中,action到action请求重定向跳转时地址栏会默认加 . action
<action name="jump2" class="com.hang.action.JumpAction2">
	  	<!-- type="chain" 代表请求转发跳转到action
	  		type="redirectAction" 代表请求重定向到action -->
	  	<result name="success" type="chain">jump1</result>
</action>
  • action跳转到另一个package下的action,其中name为固定值
<!-- 跳转到另一个package的action -->
<action name="jumpA" class="com.hang.action.JumpActionA">
	  <result name="success" type="chain">
	  		<!-- 指定目标action的namespace -->
	  		<param name="namespace">/p2</param>
	  		<!-- 指定目标action的名字 -->
	  		<param name="actionName">jumpB</param>
	  </result>
</action>
  • 全局跳转:
    • 如果当前action的配置内没有对应的result匹配视图,则去全局视图跳转位置匹配,如果全局的位置也没匹配到,则抛出异常
    • 当全局视图配置与局部冲突时,局部优先
    • 全局视图跳转的配置主要是解决跳转视图配置冗余。通常在项目后期优化使用
    • 全局视图配置只在当前package内有效
<!-- 全局视图跳转配置 -->
<package name="p3" extends="struts-default" namespace="/p3">
	  <!-- 配置全局跳转的视图 -->
	  <global-results>
	  		<result name="success">/success.jsp</result>
	  </global-results>
	  <action name="global1" class="com.hang.action.GlobalAction1">
	  		<result name="success">/error.jsp</result>
	  </action>
	  <action name="global2" class="com.hang.action.GlobalAction2">
	  </action>
	  <action name="global3" class="com.hang.action.GlobalAction3">
	  </action>
</package>
获取原生的ServletAPI
  • 获取request、session、response、application
		//使用Struts2提供的工具类获取原生ServletAPI
		HttpServletRequest request = ServletActionContext.getRequest();
		HttpSession session = request.getSession();
		HttpServletResponse response = ServletActionContext.getResponse();
		ServletContext application = ServletActionContext.getServletContext();
Struts2的action实现类创建
Struts2的实现类actionServlet的实现类service
实现类是多例的每次请求都会创建一个新的对象实现类是单例的,在tomcat中只会创建一个对象,在并发时不安全
允许定义成员变量,并且成员变量接收请求参数不允许定义成员变量
Struts2中的收参机制
  • 通过成员变量接收客户端请求参数
  • 同一个参数可以接收参数同时传递参数
  • 接收八种基本数据类型和字符串
    • 请求参数的key与action中的成员变量名对应
    • 提供对应成员变量的公开的getter / setter方法
    • 成员变量的数据类型声明为需要的类型,Struts2框架会根据需要自动完成数据类型的转换
    • 对于日期格式,必须是yyyy-MM-dd格式
<form action="${path }/ceshi/paramAction" method="post">
			username:<input type="text" name="username"><br>
			password:<input type="text" name="password"><br>
			age:<input type="text" name="age"><br>
			score:<input type="text" name="score"><br>
			birthday:<input type="text" name="birthday"><br>
			<input type="submit">
</form>
//需要提供公开的getter/setter方法
public class ParamAction implements Action{
	private String username;
	private String password;
	private Integer age;
	private Double score;
	private java.util.Date birthday;
	public java.util.Date getBirthday() {	return birthday;	}
	public void setBirthday(java.util.Date birthday) {	this.birthday = birthday;	}
	public Double getScore() {	return score;	}
	public void setScore(Double score) {	this.score = score;	}
	public Integer getAge() {	return age;	}
	public void setAge(Integer age) {	this.age = age;	}
	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;	}

	@Override
	public String execute() throws Exception {
		System.out.println("username:"+username);
		System.out.println("password:"+password);
		System.out.println("age:"+age);
		System.out.println("score:"+score);
    	System.out.println("birthday:"+birthday);
		return SUCCESS;
	}
}

<package name="ceshi" extends="struts-default" namespace="/ceshi">
		<action name="paramAction" class="com.yuan.action.ParamAction">
			<result name="success">/success.jsp</result>
		</action>
</package>
  • 接收自定义对象类型的参数
    • 在action实现类声明一个自定义类型的对象,接收请求参数
    • jsp中请求参数的key需要加把自定义类型参数名作为前缀
      action中的自定义对象变量名 . 自定义对象类型中的属性名
    • 给自定义成员变量提供getter / setter方法
<form action="${path }/ceshi/paramObjAction" method="post">
			id:<input type="text" name="employee.id"><br>
			name:<input type="text" name="employee.name"><br>
			salary:<input type="text" name="employee.salary"><br>
			age:<input type="text" name="employee.age"><br>
			<input type="submit">
</form>

public class ParamObjAction implements Action {
	private Employee employee;
	public Employee getEmployee() {	return employee;	}
	public void setEmployee(Employee employee) {	this.employee = employee;	}
	
	@Override
	public String execute() throws Exception {
		System.out.println(employee);
		return SUCCESS;
	}
}
  • 接收list集合类型:

    • 泛型为String时,key与集合名字对应
    <form action="${path }/ceshi/paramListAction" method="post">
    			爱好:
    			<input type="checkbox" name="hobby" value="eat">吃饭
    			<input type="checkbox" name="hobby" value="study">学习
    			<input type="checkbox" name="hobby" value="sleep">睡觉<br>
    			<input type="submit">
    </form>
    
    public class ParamListAction implements Action{
    	private List<String> hobby;
    	public List<String> getHobby() {	return hobby;	}
    	public void setHobby(List<String> hobby) {	this.hobby = hobby;	}
    
    	@Override
    	public String execute() throws Exception {
    		for(String str:hobby)
    			System.out.println(str);
    		return SUCCESS;
    	}
    }
    
    • 泛型为自定义类型时,不常用
    • key为action变量名[数组下标] . 自定义对象类型中的属性名
    <form action="${path }/ceshi/paramListAction" method="post">
    			id:<input type="text" name="employees[0].id">
    			name:<input type="text" name="employees[0].name">
    			salary:<input type="text" name="employees[0].salary">
    			age:<input type="text" name="employees[0].age"><br>
    			<br>
    			id:<input type="text" name="employees[1].id">
    			name:<input type="text" name="employees[1].name">
    			salary:<input type="text" name="employees[1].salary">
    			age:<input type="text" name="employees[1].age"><br>
    			<input type="submit">
    </form>
    
    public class ParamListAction implements Action{
    	private List<Employee> employees;
    	public List<Employee> getEmployees() {	return employees;	}
    	public void setEmployees(List<Employee> employees) {	this.employees = employees;	}
    
    	@Override
    	public String execute() throws Exception {
            for(Employee employee:employees){
           	 	System.out.println(employee);
    		}
    		return SUCCESS;
    	}
    }
    
使用成员变量替换request作用域
  • Struts2会自动将成员变量作为命名属性放入request作用域,命名属性就是变量名
public class RequestScopeAction implements Action{
	private String name;
	private List<String> strList;
    public List<String> getStrList() {	return strList;	}
	public void setStrList(List<String> strList) {	this.strList = strList;	}
	public String getName() {	return name;	}
	public void setName(String name) {	this.name = name;	}
	@Override
	public String execute() throws Exception {
	  //向Struts2中的一个action发送请求时,这个action的成员变量会自动存入request作用域
		name = "yuan";	//放入request作用域 名字为成员变量名,值通常在execute方法中
		strList = new ArrayList<String>();
		strList.add("hang");
		strList.add("yaun");
		return SUCCESS;
	}
}
Struts2的action另外两种开发方式
  • 继承ActionSupport,需要使用ActionSupport类的一些功能,则必须通过继承ActionSupport编写控制器

    • 底层ActionSupport实现了Action接口,不过是空实现
    //底层代码
    public class ActionSupport implements Action,...., {
          ......
           public String execute() throws Exception {
                  return SUCCESS; 
            //返回值是父类Action中的 
            //public static final String SUCCESS = "success";
              }
          .......
    }
    
    • 继承ActionSupport开发
    public class SecondAction extends ActionSupport{
    	@Override
    	public String execute()throws Exception{
    		System.out.println("action第二种开发方式");
    		return SUCCESS;
    	}
    }
    
    <action name="secondAction" class="com.yuan.action.SecondAction">
    			<result name="success">/success.jsp</result>
    </action>
    
  • 直接通过一个JavaBean作为action

    public class ThirdAction {
    	public String execute(){
    		System.out.println("第三种Action开发方式");
    		return "success";
    	}
    }
    
    <action name="thirdAction" class="com.yuan.action.ThirdAction">
    			<result name="success">/success.jsp</result>
    </action>
    
DMI开发
  • Dynamic Method Invocation,动态方法调用

  • 可以在一个action实现类中定义多个方法,每一个方法都可以处理一个客户端的请求

  • 可以减少项目中控制器类的数量,提高项目的可维护性

  • 实际开发中,通常将针对于同一张表的操作,定义在一个action实现类中。

    public class EmployeeAction extends ActionSupport{
    	
    	public String addEmployee(){
    		System.out.println("this is addEmployee");
    		return "addEmp";
    	}
    	public String queryEmployee(){
    		System.out.println("this is queryEmployee");
    		return "queryEmp";
    	}
    }
    
    <!-- 第一种DMI配置方式 -->
    <package name="emp" extends="struts-default" namespace="/emp">
    		<!-- method指定当请求这个action的时候调用哪个方法处理请求
     			method属性值:目标调用方法的名字,默认值为execute-->
    		<action name="addEmp" class="com.yuan.action.EmployeeAction" method="addEmployee">
    			<result name="addEmp">/success.jsp</result>
    		</action>
    		<action name="queryEmp" class="com.yuan.action.EmployeeAction" method="queryEmployee">
    			<result name="queryEmp">/success.jsp</result>
    		</action>
    </package>
    
    <!-- 第二种DMI配置方式 ,简化配置文件
     			name值中的 * 称为通配符,用于匹配请求路径的值,method={1}表示取name属性值第一个 * 匹配到的值,作为目标调用方法的名字
    		-->
    <!-- 第二种DMI配置
    调用:直接调用方法名即可,method通配符会自动调用方法,然后对应返回值到跳转
    http://localhost:8989/upStruts2_day3/emp/emp_addEmployee 
    http://localhost:8989/upStruts2_day3/emp/emp_queryEmployee
    -->
    <action name="emp_*" class="com.yuan.action.EmployeeAction" method="{1}">
    		<result name="addEmp">/success.jsp</result>
    		<result name="queryEmp">/success.jsp</result>
    </action>
    
Struts2配置文件中的默认值
  • 在配置文件中通常只有一个package中namespace是默认值
<!-- namespace默认值:/   -->
<package name="order" extends="struts-default">
  		<!-- method默认值:execute -->
  		<action name="orderAction" class="com.yuan.action.OrderAction">
  			<!-- name属性默认值success		type 属性默认值:dispatcher  -->
  			<result>/success.jsp</result>
  		</action>
</package>
拦截器 interceptor
  • 可以在请求到达指定action之前,进行拦截请求

  • 编写拦截器

    • 实现Interceptor接口
    public class MyInterceptor1 implements Interceptor{
    	//销毁拦截器时调用
    	public void destroy() {		}
    	//初始化拦截器调用
    	public void init() {	}
    	/*核心实现的方法
    	ActionInvocation:可以获取到目标的action对象
    	返回值:与Action实现类方法中的返回值作用一样,可以代表一个跳转的视图*/
    	public String intercept(ActionInvocation ai) throws Exception {
    		System.out.println("this is MyInterceptor1");
    		HttpServletRequest request = ServletActionContext.getRequest();
    		String name = request.getParameter("name");
      	
        //获取目标action对象,然后通过反射的方式将接收到的值设置给action对应的属性上
    		Object obj = ai.getAction();
    		System.out.println(obj.getClass());
        
    		if(name!=null){
    			//让请求继续向下执行
    			ai.invoke();
    			//返回值为null等同于不做任何跳转
    		return null;
    		}else{
    			//进行重定向跳转
    			return "error"; //代表一个跳转的视图
    		}
    	}
    }
    
    • 编写struts.xml
    <!-- 拦截器声明配置 -->
    <interceptors>
    		<!-- 声明创建一个拦截器
    		name是拦截器的别名,也是未来使用拦截器的依据
    		class是拦截器实现类的全限定名 -->
    		<interceptor name="myInterceptor1" class="com.hang.interceptor.MyInterceptor1"></interceptor>
    </interceptors>
    
    <!-- 使用拦截器 -->
    <action name="target1" class="com.yuan.action.HelloAction">
    		<!-- 指定使用的拦截器 -->
    		<interceptor-ref name="myInterceptor1"></interceptor-ref>
    		<result name="success">/success.jsp</result>
    </action>
    
  • 拦截器的使用技巧

    • 拦截器进行重定向
    <!-- 拦截器声明配置 -->
    <interceptors>			
    </interceptors>
    
    <!-- 全局视图跳转配置 -->
    <global-results>
    		<!-- 如果拦截器中的返回值表示要跳转,则使用全局视图跳转配置简化代码 -->
    		<result name="error" type="redirect">/error.jsp</result>
    </global-results>
    
    <action name="target1" class="com.yuan.action.HelloAction" method="target1">
    		<!-- 指定使用的拦截器 -->
    		<interceptor-ref name="myInterceptor1"></interceptor-ref>
    		<result name="success">/success.jsp</result>
    		<!-- 使用拦截器进行跳转,冗余代码 -->
    		<result name="error" type="redirect">/error.jsp</result>
    </action>
    
    • 多个拦截器的执行顺序,根据在action配置中的引用顺序一致
    • 拦截器栈:简化配置文件
    <!-- 拦截器声明配置 -->
    <interceptors>
    		<interceptor name="myInterceptor1" class="com.hang.interceptor.MyInterceptor1"></interceptor>
    		<interceptor name="myInterceptor2" class="com.hang.interceptor.MyInterceptor2"></interceptor>
    			
    		<!-- 配置拦截器栈 name为拦截器栈的名字,是引用拦截器栈的依据-->
    		<interceptor-stack name="myDefaultStack">
    				<interceptor-ref name="myInterceptor1"></interceptor-ref>
    				<interceptor-ref name="myInterceptor3"></interceptor-ref>
    		</interceptor-stack>
    </interceptors>
    
    <!-- 引用使用的拦截器栈 -->
    <action name="target1" class="com.yuan.action.HelloAction" method="target1">
    		<!-- 指定使用的拦截栈,相当于使用了拦截器栈中拦截器的功能 -->
    		<interceptor-ref name="myDefaultStack"></interceptor-ref>
    		<result name="success">/success.jsp</result>		
    </action>
    
    • default-interceptor-ref 默认使用拦截器或拦截器栈
    <!-- 写在 interceptors 标签后 -->
    <!-- 指定当前package内所有的action默认引用的拦截器或拦截器栈 -->
    <default-interceptor-ref name="myDefaultStack"></default-interceptor-ref>
    
    • 通常书写的package都继承了系统的struts-default package,即继承了struts-default里面的功能,当自己设置了默认的拦截器栈后,会覆盖系统拦截器栈道引用
    • 如果覆盖掉系统的拦截器栈,则会失去系统拦截器栈提供的功能;所有在使用自己的拦截器栈时,必须手动引入系统的默认拦截器栈
    <!-- 配置拦截器栈 name为拦截器栈的名字,是引用拦截器栈的依据-->
    <interceptor-stack name="myDefaultStack">
    			<interceptor-ref name="myInterceptor"></interceptor-ref>
    			<!-- 引用系统的默认拦截器栈 -->
    			<interceptor-ref name="defaultStack"></interceptor-ref>
    </interceptor-stack>
    
  • 方法拦截器

    • 针对action实现类中的方法制定拦截规则,使用时和普通拦截器一样
    <!-- 配置拦截器 -->
    <interceptors>
        <interceptor name="myMethodInterceptor" class="com.hang.interceptor.MyMethodInterceptor">
                <!-- 配置方法的拦截规则:只根据方法名来判断是否拦截
                 name="includeMethods" 包含哪些方法拦截
                 name="excludeMethods" 不拦截哪些方法
            方法拦截规则是针对于方法的配置,与方法在哪个类没有直接关系-->
                <param name="excludeMethods">mb,mc</param>
         		 <!-- 方法名的配置支持通配符 ,拦截以m开头的方法 -->
    			<param name="includeMethods">m*</param>
        </interceptor>
        <interceptor-stack name="myDefaultStack">
                <interceptor-ref name="defaultStack"></interceptor-ref>
                <interceptor-ref name="myMethodInterceptor"></interceptor-ref>
        </interceptor-stack>
    </interceptors>
    
    <action name="emp_*" class="com.hang.action.EmpAction" method="{1}">
            <!-- 引用拦截器 -->
            <interceptor-ref name="myDefaultStack"></interceptor-ref>
            <result name="ma">/success.jsp</result>
            <result name="mb">/success.jsp</result>
            <result name="mc">/success.jsp</result>
    </action>
    
拦截器使用的标准流程
  • 实现Interceptor接口

  • 编写配置文件,默认拦截器有效范围在当前的package内,也可以通过package的继承扩大拦截器的使用范围

    <package name="employee" extends="struts-default" namespace="/employee">
    		<!-- 拦截器配置 -->
    		<interceptors>
    				<!-- 拦截器声明配置 -->
    				<interceptor name="myInterceptor" class="com.hang.interceptor.MyInterceptor"></interceptor>
    				<!-- 拦截器栈配置 -->
    				<interceptor-stack name="myDefaultStack">
    						<!-- 引用系统默认的拦截器栈 -->
    						<interceptor-ref name="defaultStack"></interceptor-ref>
    						<interceptor-ref name="myInterceptor"></interceptor-ref>
    				</interceptor-stack>
    		</interceptors>
    		
    		<action name="findAll" class="com.hang.action.FindAllEmployeesAction">
    				<!-- 拦截器引用 -->
    				<intercepter-ref name="myDefaultStack"></intercepter-ref>
    				<result name="emplist">/emplist.jsp</result>
    		</action>
    </package>
    
Struts2中POST方式请求中文的编码处理
  • Struts2默认已经处理了POST请求提交数据的中文乱码,默认编码为UTF-8,默认的以键值对存储

    • 自定义编码格式,只针对POST请求 i18n是资源国际化的缩写
    <!-- constant标签用于自定义Struts2的一些初始化配置 -->
    在struts.xml中覆盖<constant></constant>标签,用于覆盖struts2默认配置,以key-value存储
    <!-- 自定义编码格式 -->
    <constant name="struts.i18n.encoding" value="iso-8859-1">
    </constant>
    
    • 在struts2中的struts.xml中进行GET方式拼接传参
    <action name="login" class="com.yuan.action.LoginAction" >
    		<result name="success">/success.jsp</result>
    		<result name="login" type="redirect">
    				<!-- 重定向跳转get拼接传参 -->
    				<param name="location">/login.jsp</param>
    				<!-- login.jsp?errorMsg=验证账户信息失败 -->
    				<param name="errorMsg">验证用户信息失败</param>
    		</result>
    </action>
    
验证码
  • 编码实现

    • 生成验证码随机数
    • 将随机数绘制到验证码图片上
    public class CaptchaAction {
    	
    	public String execute() throws IOException{
    		//生成验证码随机数
    		String securityCode = SecurityCode.getSecurityCode();
      	  //将随机数存入session,做验证使用
    		HttpSession session = ServletActionContext.getRequest().getSession();
    		session.setAttribute("securityCode", securityCode);
        
    		//绘制生成验证码图片
    		BufferedImage image = SecurityImage.createImage(securityCode);
    		
    		//使用IO流响应结果到客户端
    		HttpServletResponse response = ServletActionContext.getResponse();
    		OutputStream out = response.getOutputStream();
    		/* ImageIO工具类,写验证码图片使用
    		 * 第一个参数:指定验证码图片对象
    		 * 第二个参数:指定图片的格式
    		 * 第三个参数:指定输出流
    		 */
    		ImageIO.write(image, "png", out);
    		
    		return null;//返回null代表不做跳转
    	}
    }
    
    <!-- 验证码配置 -->
    <action name="captcha" class="com.yuan.action.CaptchaAction">
    			<!-- type=stream 代表以IO流的方式响应结果 -->
    			<result type="stream">image/png</result>
    </action>
    
    • 使用验证码,并从session中获取服务器端生成的验证码随机数,与客户端提交的进行对比
    验证码:
    <input type="text" name="captchaCode"><br>  <!-- 用户输入 -->
    <img src="${pageContext.request.contextPath }/p1/captcha"/>  <!-- 验证码图片 -->
    
文件上传
  • 客户端HTML编写

  • 服务器编写代码接收

    //客户端浏览器
    <!-- 将选择的文件,以二进制流的形式提交到服务器 并且必须以POST方式提交数据 -->
    <form action="${path }/p1/uploadAction" methed="post" enctype="multipart/form-data" > 
    		<input type="file" name="upload">
    		<input type="submit" value="upload">
    </form>
    
    /* 服务器端
    1,接收上传文件 .File
    2,将上传的文件保存,保存在服务器的某个位置 */
    class UploadAction{
    	private File upload;
    	//上传文件的key+FileName,获取上传文件的原始文件名
    	private String uploadFileName;
    	getter/setter
    	public String execute(){
    		//使用IO流处理上传文件的保存,保存到服务器端
    	}
    }
    
  • 文件上传实现:Struts2把文件封装

    • 第一版
    public String execute() throws IOException{
    		System.out.println("文件名:"+uploadFileName);
    
    		InputStream in = new FileInputStream(upload);
    		OutputStream out = new FileOutputStream("C:\\Learn\\upStruts2\\upStruts2_day4\\WebRoot\\upload\\"+uploadFileName);
    		//缓冲区
    		byte[] buffer = new byte[1024];
    		int len = 0;
    		while(true){
    			//每次读取的放到缓冲区中
    			len = in.read(buffer);
    			if(len==-1) break;
    			//写到服务器指定位置保存
    			out.write(buffer, 0, len);
    		}
    		//释放资源
    		out.close();
    		in.close();
    		
    		return "success";
    	}
    }
    
    • 获取上传文件的原始类型
    //获取上传文件原始类型,指文件的互联网类型MIME类型
    //格式:父类型 / 子类型
    //变量名:上传文件的key+ContentType
    private String uploadContentType;
    
    • 第二版,使用工具类
    public String execute() throws IOException{
    		System.out.println("文件名:"+uploadFileName);
    		
    		//根据相对路径,获取保存文件的真实路径
    		ServletContext servletContext = ServletActionContext.getServletContext();
    		String realPath = servletContext.getRealPath("upload");
    		System.out.println("保存上传文件的真实路径:"+realPath);
    		System.out.println("上传文件原始文件类型:"+uploadContentType);
    		//将上传的文件保存到realPath下
    		//参数1:上传过来的文件对象
    		//参数2:目标保存上传文件的文件对象
    		FileUtils.copyFile(upload, new File(realPath+"\\"+uploadFileName));
    		
    		return "success";
    }
    
    • 保证上传到服务器的文件,保存的名字唯一
    • UUID是一个全球唯一的字符串序列,32位,可以使用UUID作为上传的文件名
    UUID uuid = UUID.randomUUID();
    System.out.println(uuid);
    
    
    public String execute() throws IOException{
    		//根据相对路径,获取保存文件的真实路径
    		ServletContext servletContext = ServletActionContext.getServletContext();
    		String realPath = servletContext.getRealPath("upload");
    		System.out.println("文件名:"+uploadFileName);
    		System.out.println("保存上传文件的真实路径:"+realPath);
    		System.out.println("上传文件原始文件类型:"+uploadContentType);
    		
    		//使用UUID实现保存文件的名字
    		UUID uuid = UUID.randomUUID();
    		//获取上传文件的后缀
    		String extension = FilenameUtils.getExtension(uploadFileName);
    		System.out.println("文件的后缀:"+extension);
    		//组成新的文件名
    		String saveFileName = uuid.toString()+"."+extension;
    		//将上传的文件保存到realPath下
    		//参数1:上传过来的文件对象
    		//参数2:目标保存上传文件的文件对象
    		FileUtils.copyFile(upload, new File(realPath+"/"+saveFileName));
    		
    		return "success";
    }
    
    • 修改上传文件大小的限制
    Struts2默认大小struts.multipart.maxSize=2097152
    
    <!-- 设置上传文件的大小,在配置文件中覆盖struts2默认属性 -->
    <constant name="struts.multipart.maxSize" value="15728640">
    </constant>
    
  • 文件下载

    /*提供一个读取服务器文件的输入流
    	Struts2框架会自动调用这个方法,然后把输入流读取到的文件下载到客户端
    	*/
    public class DownLoadAction extends ActionSupport{
    	/*提供一个读取服务器文件的输入流
    	Struts2框架会自动调用这个方法,然后把输入流读取到的文件下载到客户端
    	*/
      	//默认调用execute方法,所以可以写空实现或者继承ActionSupport
    	public InputStream getInputString() throws FileNotFoundException{
    		String filePath = "C:\\apache-tomcat-7.0.79\\webapps\\upStruts2_day4\\upload\\wallhaven-2eex5x.jpg";
    		return new FileInputStream(filePath);
    	}
    }
    
    <!-- 文件下载第一版 -->
    <action name="download" class="com.yuan.action.DownLoadAction">
    		<!-- type="stream"表示使用IO流相应结果 -->
    		<result type="stream">
    				<!-- 设置下载文件的类型,要互联网类型 -->
    				<param name="contentType">image/jpeg</param>
    				<!-- 设置下载文件的保存方式,两种
    				一是以附件的形式保存到磁盘 attachment
    				二是直接在客户端浏览器打开,inline 浏览器必须支持这种文件类型 -->
    				<param name="contentDisposition">inline;filename=x.jpg</param>
    				<!-- 设置下载文件的缓冲区大小,面向缓冲区操作,提高读写效率 -->
    				<param name="buffer">1024</param>
    		</result>
    </action>
              
    
    
    • 优化下载名字:使用action的成员变量给struts.xml传参
    <!-- 文件下载第二版 -->
    public class DownLoadAction extends ActionSupport{
    	private String filename;
    	public String getFilename() {	return filename;	}
    	public void setFilename(String filename) {	this.filename = filename;	}
    
    	/*提供一个读取服务器文件的输入流
    	Struts2框架会自动调用这个方法,然后把输入流读取到的文件下载到客户端
    	*/
    	public InputStream getInputStream() throws FileNotFoundException{
    		ServletContext servletContext = ServletActionContext.getServletContext();
    		String realPath = servletContext.getRealPath("/upload");
    		return new FileInputStream(realPath+"/wallhaven-2eex5x.jpg");
    	}
    	public String execute(){
    		filename="3546.jpg"; 	//对下载文件重命名
    		return "success";
    	}	
    }
    
    <!-- 文件下载 -->
    <action name="download" class="com.yuan.action.DownLoadAction">
    		<!-- type="stream"表示使用IO流相应结果 -->
    		<result type="stream">
    				<!-- 设置下载文件的类型,要互联网类型 -->
    				<param name="contentType">image/jpeg</param>
    				<!-- 设置下载文件的保存方式,两种
    				一是以附件的形式保存到磁盘 attachment
    				二是直接在客户端浏览器打开,inline 浏览器必须支持这种文件类型 -->
     				 <!-- 使用OJNL表达式动态获取名字 -->
    				<param name="contentDisposition">attachment;filename=${filename}</param>  
    				<!-- 设置下载文件的缓冲区大小,面向缓冲区操作,提高读写效率 -->
    				<param name="buffer">1024</param>
    		</result>
    </action>
    
Struts2配置文件拆分
<!-- 主配置文件struts.xml -->
<include file="com/hang/conf/struts-user.xml"></include>

<!-- 副配置文件struts-user.xml -->
<!-- 如果当前配置文件中跳转到另一个配置文件的某个action,则需要将其包含 -->
<include file="com/hang/conf/struts-emp.xml"></include>
DTO
  • data transfer object,数据传输对象
  • 主要用于数据的传递
MainDTO{
	private List<Book> list;
	private List<Book> list1;
	private List<Book> list2;
  
	getter / setter
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值