Struts2学习笔记(二)
1. 自定义结果视图的类型(结果视图类型的应用)
CAPTCHA图像(随机验证码图像)
实现步骤:
(1)编写一个类实现com.opensymphony.xwork2.Result, 或者继承org.apache.struts2.dispatcher.StrutsResultSupport
package com.itheima.results; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.OutputStream; import java.util.Random; import javax.imageio.ImageIO; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.Result; public class CaptchaResult implements Result { private int width = 100; private int height = 25; public void setWidth(int width) { this.width = width; } public void setHeight(int height) { this.height = height; } public void execute(ActionInvocation invocation) throws Exception { //BufferedImage:代表内存图片 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //Graphics:画笔 Graphics g = image.getGraphics(); //画边线 g.setColor(Color.GREEN); g.drawRect(0, 0, width, height); //填充背景色 g.setColor(Color.YELLOW); g.fillRect(1, 1, width-2, height-2); //干扰线 Random r = new Random(); g.setColor(Color.GRAY); for(int i=0;i<15;i++) g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height)); //验证码 g.setColor(Color.RED); g.setFont(new Font("宋体",Font.BOLD|Font.ITALIC, 18)); int x = 19; for(int i=0;i<4;i++){ g.drawString(r.nextInt(10)+"",x, 20); x+=20; } //ImageIO:输出图片给指定的流 OutputStream out = ServletActionContext.getResponse().getOutputStream(); ImageIO.write(image, "jpg", out); } } |
. (2)在struts.xml 文件中声明定义的结果视图类型
<struts> <constant name="struts.devMode" value="true" /> <package name="default" extends="struts-default"> <result-types> <result-type name="captcha" class="com.itheima.results.CaptchaResult"></result-type> </result-types> <action name="genCaptcha"> <result name="success" type="captcha"> <param name="width">200</param>----//这里可以动态改变验证码视图的长宽比例 <param name="height">50</param> </result> </action> </package> </struts> |
(3). Jsp视图显示内容
<form action = ""> 用户名:<input type= "text" name = "username"></input> 验证码:<input type = "text" name = "code"><img src="${pageContext.request.contextPath}/genCaptcha.action"/> </form> |
(4). 效果截图如下
2. 封装参数到JavaBean或action中(重点)
(1)Action或JavaBean中接收请求参数
两种方式:
(1)在动作类中成员变量给予初始值,即动作类中的字段有默认值
(2)在配置文件中注入动作类的参数值(静态参数设置)
步骤如下:
1)定义一个PersonAction类
public class PersonAction extends ActionSupport { private String name = "刘小晨"; private String password; private int age; public String getName() { return name; } public void setName(String name) { System.out.println("调用了setName方法"); this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String execute() throws Exception { System.out.println(name+":"+password+":"+age); return SUCCESS; } } 这里要为个成员变量设置相应的getter和setter方法 |
2)struts.xml文件的配置
<action name = "action1" class ="com.itheima.actions.PersonAction"> <param name ="name">崔召金</param> <param name ="age">24</param> <param name ="password">201026</param> <result>/result.jsp</result> </action> 访问:http://localhost:8080/day27_01_struts2Params/action1 网页返回结果如下 崔召金:201026:24 另外控制台打印出如下内容: 调用了setName方法 调用了setName方法 崔召金:201026:24 这里说明了,action自己给初始化name调用setName方法执行一次,另外一次是编码人员自己在sturts.xml中给name赋值时,又执行一次。 实际上是由一个叫做staticParams的拦截器做的(具体里面干了什么事,我真心搞不懂) |
(3)表单请求参数,动态参数的注入(params)
方式一:用动作类作为模型action
Jsp页面内容:
<form action="${pageContext.request.contextPath}/action2" method="post"> 用户名:<input type="text" name="name"/><br/> <input type ="submit" value="保存"/> </form> |
Struts.xml文件中的配置如下:
<action name = "action2" class ="com.itheima.actions.PersonAction1"> <param name ="name">崔召金a</param> <result>/result.jsp</result> </action> |
PersonAction1
public class PersonAction1 extends ActionSupport { private String name = "刘小晨"; public String getName() { return name; } public void setName(String name) { System.out.println("调用了setName方法"); this.name = name; } public String execute() throws Exception { System.out.println(name); return super.execute(); } } 总结:表单的字段输入域的name取值要和动作类的写属性名称一致。 结果如下: 调用了setName方法 调用了setName方法 Java编程经典 |
方式二:动作类和模型分开
Student类
package com.itheima.domain; import java.io.Serializable; public class Student implements Serializable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } } |
StudentAction动作类
package com.itheima.actions; import com.itheima.domain.Student; import com.opensymphony.xwork2.ActionSupport; public class StudentAction extends ActionSupport { private Student student = new Student(); public Student getStudent() { System.out.println("调用了getStudent方法"); return student; } public void setStudent(Student student) { System.out.println("调用了setStudent方法"); this.student = student; } public String execute() throws Exception { System.out.println(student); return NONE; } |
Jsp页面如下配置
<form action="${pageContext.request.contextPath}/saveStudent" method="post"> 用户名: <input type="text" name="student.name"/><br/>崔召金 年龄: <input type="text" name="student.age"/><br/>23 <input type="submit" value="保存"/> </form> |
Sturts.xml文件配置代码如下:
<action name="saveStudent" class="com.itheima.actions.StudentAction"/> |
控制台打印代码如下:
调用了getStudent方法
调用了getStudent方法
Student [name=崔召金, age=23]
执行的过程:
框架再调用StudentAction的getStudent(),方法,得到刚刚创建的对象
紧着着,调用student实例的setName和setAge方法,设置值。
方式三:模型驱动(ModelDriven)与valueStack有关
(1)使用模型驱动,关键是动作类的编写
package com.itheima.actions; import com.itheima.domain.Customer; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; //使用模型驱动:实现modeldrivern接口 public class CustomerAction extends ActionSupport implements ModelDriven<Customer>{ private Customer customer = new Customer(); public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } @Override public String execute() throws Exception { System.out.println(customer); return NONE; } public Customer getModel() { return customer; } } |
Customer类的编写
package com.itheima.domain; import java.io.Serializable; public class Customer implements Serializable { private String name; private String city; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return "Customer [name=" + name + ", city=" + city + "]"; } } |
Jsp页面代码编写
<form action="${pageContext.request.contextPath}/saveCustomer.action" method="post"> 用户名:<input type="text" name="name"/><br/> 城市:<input type="text" name="city"/><br/> <input type="submit" value="保存"/> </form> |
Sturts.xml中的配置
<action name="saveCustomer" class="com.itheima.actions.CustomerAction"/> |
原理:实际上由一个拦截器来完成的modelDriven 该拦截器会在执行动作方法前,把模型对象压到ValueStack值栈的栈顶。 控制台输出如下内容: Customer [name=崔召金, city=上海] |
3. 封装数据到Collection中
JSP页面代码
<body> <form action="${pageContext.request.contextPath}/collectionAction1" method="post"> 爱好: <input type="checkbox" name="hobby" value="吃饭"/>吃饭 <input type="checkbox" name="hobby" value="睡觉"/>睡觉 <input type="checkbox" name="hobby" value="学java"/>学java <input type="submit" value="保存"/> </form> <hr/> <h2>批量添加员工信息</h2> <form action="${pageContext.request.contextPath}/collectionAction2" method="post"> 员工1:姓名:<input type="text" name="employees[0].name"/>薪水:<input type="text" name="employees[0].salary"/><br/> 员工2:姓名:<input type="text" name="employees[1].name"/>薪水:<input type="text" name="employees[1].salary"/><br/> 员工3:姓名:<input type="text" name="employees[2].name"/>薪水:<input type="text" name="employees[2].salary"/><br/> <input type="submit" value="保存"/> </form> <h2>向Map中添加内容</h2> <form action="${pageContext.request.contextPath}/collectionAction3" method="post"> 员工1:姓名:<input type="text" name="emps['e1'].name"/>薪水:<input type="text" name="emps['e1'].salary"/><br/> 员工2:姓名:<input type="text" name="emps.e2.name"/>薪水:<input type="text" name="emps.e2.salary"/><br/> 员工3:姓名:<input type="text" name="emps.e3.name"/>薪水:<input type="text" name="emps.e3.salary"/><br/> <input type="submit" value="保存"/> </form> </body> |
Struts.xml文件的配置
<action name="collectionAction1" class="com.itheima.actions.CollectionAction"/> <action name="collectionAction2" class="com.itheima.actions.CollectionAction"/> <action name="collectionAction3" class="com.itheima.actions.CollectionAction"/> |
Employee类的编写
package com.itheima.domain; import java.io.Serializable; public class Employee implements Serializable { private String name; private float salary; public String getName() { return name; } public void setName(String name) { this.name = name; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } @Override public String toString() { return "Employee [name=" + name + ", salary=" + salary + "]"; } } |
4. 自定义类型转换器:
对于大部分常用类型,开发者根本无需创建自己的转换器,Struts2内置了常见数据类型多种转换器
boolean 和 Boolean
char和 Character
int 和 Integer
long 和 Long
float 和 Float
double 和 Double
Date 可以接收 yyyy-MM-dd格式字符串
数组 可以将多个同名参数,转换到数组中
集合 支持将数据保存到 List 或者 Map 集合
java.util.Date类型的属性可以接收格式为2009-07-20的请求参数值。但如果我们需要接收格式为20091221的请求参数,我们必须定义类型转换器,否则struts2无法自动完成类型转换。 |
自定义类型转换器的编写:
1、编写一个类,继承StrutsTypeConverter
package com.itheima.convertor; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import org.apache.struts2.util.StrutsTypeConverter; //日期转换器: /* * String :12/31/2001 ---->Date * Date---------->String:12/31/2001 */ public class MyDateConvertor extends StrutsTypeConverter { private DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); //从字符串转换成日期 public Object convertFromString(Map context, String[] values, Class toClass) { if(toClass==Date.class){ String value = values[0];//获取用户输入的参数值12/31/2001 try { return df.parse(value); } catch (ParseException e) { e.printStackTrace(); } } return null; } //日期转换成字符串 public String convertToString(Map context, Object o) { if(o instanceof Date){ Date d = (Date)o; return df.format(d); } return null; } } |
注册类型转换器 |
1.局部类型转换器
在action动作类所在的包中,新建一个名字为actionName-conversion.properties的文件
内容如下:
birthday=com.itheima.convertor.MyDateConverter(局部类型转换器)
2.全局类型转换器:
在web-inf/classes目录下新建一个名为:xwork-convesion.properties;
内容 如下:
java.util.Date =com.itheima.convertor.MyDateconverter
3.类型转换中的类型提示:
出现转换失败时的错误提示(conversionError拦截器完成的)
Struts.xml文件中的内容如下:
<struts> <constant name="struts.devMode" value="true" /> <package name="default" extends="struts-default"> <action name ="addUser" class = "com.itheima.actions.UserAction" method = "add"> <result>/success.jsp</result> <result name="error">/error.jsp</result> <result name="input">/regist.jsp</result> </action> </package> </struts> |
Jsp页面中需要改动的地方:
<%@ taglib uri="/struts-tags" prefix="s"%> |
。。。。。。。 |
<body> <s:fielderror></s:fielderror> 。。。。。。。。。 |
出现转换失败,会被转向一个叫input的视图,并把错误信息提示封装到fieldError中。
在动作类所在的包中建立一个名称为“动作类名.properties”的配置文件,
内容如下:
invalid.fieldvalue.字段=你的提示信息 |
效果如下:
5.struts里的输入校验:
1. 输入校验
客户端校验 过滤正常用户的误操作,通过JS代码完成
服务器端校验,整个应用阻止非法数据的最后防线
2. 验证的途径:
1.编程式验证
userAction中覆盖方法
@Override public void validate() { //用户名不能为null或“” if(StringUtils.isEmpty(username)){ addFieldError("username", "用户名不能为空");// Map<String,String>key:字段名,value错误提示 } } |
如上一个方法的作用范围为所有的action动作。如果想只约束某个动作可以这么做。
<action name ="addUser" class = "com.itheima.actions.UserAction" method = "add"> <result name = "success">/success.jsp</result> <result name="error">/error.jsp</result> <result name="input">/regist.jsp</result> </action> <action name="UserEdit" class="com.itheima.actions.UserAction" method="edit"> <result>/success.jsp</result> <result name="error">/error.jsp</result> <result name="input">/edit.jsp</result> </action> |
方式一:对指定的动作让其或略验证:
@SkipValidation public String edit() { return SUCCESS; } |
方式二:只针对edit方法进行验证!
public String edit() { return SUCCESS; } public void validateEdit() { if(StringUtils.isEmpty(username)){ addFieldError("username", "用户名不能为空哦哦"); } } |
如上所示 : 只会对edit()方法做出验证: 验证不通过时,框架会给你转向一个叫做input的视图,使用struts2的<s:fieldError/>显示错误消息提示。 |
2. 声明式验证
针对所有方法进行验证:
在动作类所在的包中,建立一个名称为”动作类名-validation.xml”
内容如下:
针对某个动作进行验证:
在动作类所在的包中,建立一个名称为”动作类名-动作名称-validation.xml”
或者使用@SkipValidation
6 Struts2框架中声明式验证的内置验证器
他们都在xwork-core-*.jar包的
com.opensymphony.xwork2.validator.validators.default.xml中进行了定义。
常用的验证器:
requiredstring:字符串不能为null或空字符串。默认情况下会对数据进行trim后进行判断。
<!-- 写法形式一:可以给一个字段添加好多的验证规则 -->
<!-- 写法形式一:可以给一个字段添加好多的验证规则 --> <field name = "username"><!-- 验证的字段名称 --> <field-validator type="requiredstring"><!-- 不能为空 --> <param name = "trim">false</param> <message>用户名不能为空,你找骂</message> </field-validator> <field-validator type="stringlength"><!-- 验证字符串长度的 --> <param name="minLength">3</param> <param name="maxLength">9</param> <message>用户名必须介于${minLength}~${maxLength}之间哦</message> </field-validator> </field> |
<!-- 写法形式二:用于非字段性的验证
<validator type="requiredstring"> <param name="fieldName">username</param> <message>用户名不能为空</message> </validator> |
7 自定义声明式验证器
1.编写一个类,继承com.opensymphony.xwork2.validator.validators.FieldValidatorSupport
StrongPasswordValidator类
package com.itheima.actions; import com.opensymphony.xwork2.validator.ValidationException; import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport; public class StrongPasswordValidator extends FieldValidatorSupport { private int minLength = -1; public int getMinLength() { return minLength; } public void setMinLength(int minLength) { this.minLength = minLength; } //验证方法:针对不符合要求的内容进行判断,向Map中添加信息即可 //参数:object就是当前的动作对象 public void validate(Object object) throws ValidationException { //获取要验证的字段名称 String fieldName = getFieldName(); Object fieldValue = getFieldValue(fieldName, object); if(fieldValue==null) return; if(!isStrong((String)fieldValue)){ addFieldError(fieldName, object); } if((minLength>-1)&&((String)fieldValue).length()<minLength){ // 添加一个消息 addFieldError(fieldName, object); } } //判断s是否强大 private static final String GROUP1 = "abcdefghijklmnopqrstuvwxyz"; private static final String GROUP2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final String GROUP3 = "0123456789"; //判断密码是否强壮:至少一个大写字母、一个小写字母、一个数字 private boolean isStrong(String s) { boolean ok1 = false; boolean ok2 = false; boolean ok3 = false; int length = s.length(); for(int i=0;i<length;i++){ if(ok1&&ok2&&ok3) break; String character = s.substring(i,i+1); if(GROUP1.contains(character)){ ok1 = true; continue; } if(GROUP2.contains(character)){ ok2 = true; continue; } if(GROUP3.contains(character)){ ok3 = true; continue; } } return ok1&&ok2&&ok3; } } |
2.在WEB-INF\classes目录下建立一个固定名称validators.xml的配置文件,
3.UserAction-UserEdit-validation.xml中写如下代码:
<field name ="password"> <field-validator type ="strongpassword"> <message>您的密码不够强壮</message> </field-validator> </field> |
验证两次密码是否一致
1.jsp中如下代码:
<s:actionerror>
2.UserAction-UserEdit-validation.xml中写如下代码:
<validator type="expression"> <param name="expression"> password==repassword </param> <message>两次密码必须一致</message> </validator> |
8. Struts2的国际化
1、配置全局国际化消息资源包
a.配置全局消息资源包
b、如何访问
l 在动作类中:
前提,动作类继承ActionSupport
l 在页面中:
或者
<s:text name="hello"></s:text>
l *指定消息资源包,借助struts2的有关国际化的标签:
如果消息资源包在com.itheima.resources.msg_zh_CN.properties
2. 配置局部消息资源包
一定要经过Action才行:
书写规范:在动作类所在包中,建立名字”动作类名-zh-CN.properties”的配置文件。动作类中访问,发现局部的比全局的优先级高。
3、包范围的消息资源包
也得经过action访问
书写有规范的,名称为package_zh_CN.properties,放在类的包中。可以被包中及子包的所有动作类来访问。
总结:
day27_00_struts2Result
day27_01_struts2Params
day27_02_struts2regist
day27_03_struts2i18n
9. struts2中的拦截器(框架功能核心)
1.过滤器与拦截器的对比
功能是一回事。
过滤器是Servlet规范中的技术,可以对请求和响应进行过滤。
拦截器是Struts2框架中的技术,实现AOP(面向切面)的编程思想,可以对请求动作进行拦截。
2. 自定义拦截器
步骤:
A. 编写一个类,直接或间接实现Interceptor接口。(拦截器会驻留内存),一般都选择继承AbstractInterceptor
public class MyInterceptor extends AbstractInterceptor { public String intercept(ActionInvocation invocation) throws Exception { System.out.println("拦截器执行前"); String result = invocation.invoke(); System.out.println("拦截器执行后"+result); return result; } } |
B. 需要在struts.xml中进行定义
C. 在动作配置中就可以使用了
知识点:除了要使用自定义的拦截器之外,还要使用defaultStack,可以这么办
方法一:(自己使用)
方法二:(大家都用的时候)
<package name="mydefault" extends="struts-default"> <interceptors> <interceptor name="myinterceptor" class="com.itheima.interceptor.MyInterceptor"></interceptor> <interceptor-stack name="mydefaultStack"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="myinterceptor"></interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="myinterceptor"></default-interceptor-ref> </package> <package name="p1" extends="mydefault"> <action name="action1" class = "com.itheima.action.MyInterceptorAction" method="add"> <result>/success.jsp</result> </action> </package> |
3. 能够指定拦截的方法或不拦截的方法(自定义拦截器)
自定义拦截指定的方法是指对拦截器进行限制,对需要拦截的动作action进行拦截,具体步骤如下:
1.写一个action类CustomerAction类
package com.itheima.action; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class CustomerAction extends ActionSupport { public String add() { System.out.println("调用add的service方法"); return SUCCESS; } public String edit() { System.out.println("调用edit的service方法"); return SUCCESS; } public String login() { System.out.println("登录了哦"); ServletActionContext.getRequest().getSession().setAttribute("user","ppp" ); return SUCCESS; } } |
2.编写一个拦截器类MyInterceptor1
public class MyInterceptor1 extends MethodFilterInterceptor { protected String doIntercept(ActionInvocation invocation) throws Exception { System.out.println("拦截了,嘿嘿"); return invocation.invoke(); } } |
3. struts.xml文件中这样编写:
<package name="p1" extends="struts-default"> <interceptors> <!-- 自定义一个拦截器 --> <interceptor name="myinterceptor" class="com.itheima.interceptor.MyInterceptor"></interceptor> <interceptor name="myinterceptor1" class="com.itheima.interceptor.MyInterceptor1"></interceptor> </interceptors> <action name="action1" class = "com.itheima.action.MyInterceptorAction" method="add"> <interceptor-ref name="defaultStack"></interceptor-ref> 将默认的拦截器个补进来 <interceptor-ref name="myinterceptor"></interceptor-ref> 自定义拦截器类 <result>/success.jsp</result> </action> <action name="*" class ="com.itheima.action.CustomerAction" method="{1}"> <interceptor-ref name="myinterceptor1"> <param name="excludeMethods">edit</param> 将剑代码,在拦截器中俄一个参数excludedMethods=需要排除的动作 </interceptor-ref> <result>/success.jsp</result> </action> </package> |
10. 文件啊文件上传与下载
1、前提:
form表单的method必须是post
form表单的enctype必须是multipart/form-data
提供type=”file”的上传输入域.
2.单文件上传
JSP页面代码:
<body> <s:actionerror/> <hr/> <s:fielderror></s:fielderror> <form action="${pageContext.request.contextPath}/upload1.action" method="post" enctype="multipart/form-data"> 用户名:<input type="text" name="username"/><br/> 靓照:<input type="file" name="photo"/><br/> <input type="submit" value="上传"/> </form> </body> |
Struts.xml文件代码如下:
<struts> <constant name="struts.devMode" value="true" /> <constant name="struts.custom.i18n.resources" value="globa"></constant>配置全局的资源信息。(为信息回显做翻译) <package name="p1" extends="struts-default" > <action name="upload1" class="com.itheima.action.UploadAction1" method="upload"> <interceptor-ref name="defaultStack"> <param name="fileUpload.allowedTypes">image/jpeg,image/png</param>允许上传的类型 <param name="fileUpload.allowedExtensionsSet">jpg,jpeg,png</param>允许上传文件的后缀名字 </interceptor-ref> <result>/success.jsp</result> <result name="error">/error.jsp</result> <result name="input">/index.jsp</result> </action> </package> </struts> |
核心action类编写如下:
package com.itheima.action; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; //文件上传:fileUpload拦截器完成的 public class UploadAction1 extends ActionSupport { private String username; //text文本域中的名称保持一致 private File photo; //和表单的上传字段名保持一致。类型是File类型的 private String photoFileName; //上传的文件名 private String photoContentType; //上传文件的MIME类型(这里contentType和上传字段photo合作通过拦截器得到类型) public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public File getPhoto() { return photo; } public void setPhoto(File photo) { this.photo = photo; } public String getPhotoFileName() { return photoFileName; } public void setPhotoFileName(String photoFileName) { this.photoFileName = photoFileName; } public String getPhotoContentType() { return photoContentType; } public void setPhotoContentType(String photoContentType) { //System.out.println("调用getPhotoContentType方法"+photoContentType); this.photoContentType = photoContentType; } @Override public String toString() { return "UploadAction1 [username=" + username + ", photo=" + photo + ", photoFileName=" + photoFileName + ", photoContentType=" + photoContentType + "]"; } public String upload() { System.out.println(photoFileName+"(上传文件的名)"+photoContentType+"(上传文件的MIME类型)"); System.out.println(username); //上传字段:上传到某个文件夹。存到应用的images目录下 String realPath = ServletActionContext.getServletContext().getRealPath("/files"); File file = new File(realPath); if(!file.exists()) { file.mkdirs(); } try { FileUtils.copyFile(photo, new File(file, photoFileName)); System.out.println(this.toString()); return SUCCESS; } catch (IOException e) { e.printStackTrace(); return ERROR; } } } |
3. 多文件上传于单文件上传的关系区别不是太大,关键是action类的编写:
uploadAction2.java文件的编写如下所示:
package com.itheima.action; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; //文件上传:fileUpload拦截器完成的 public class UploadAction2 extends ActionSupport { private String username; private File[] photo;//和表单的上传字段名保持一致。类型是File类型的 .数组或List private String[] photoFileName;//上传的文件名 private String[] photoContentType;//上传文件的MIME类型 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public File[] getPhoto() { return photo; } public void setPhoto(File[] photo) { this.photo = photo; } public String[] getPhotoFileName() { return photoFileName; } public void setPhotoFileName(String[] photoFileName) { this.photoFileName = photoFileName; } public String[] getPhotoContentType() { return photoContentType; } public void setPhotoContentType(String[] photoContentType) { this.photoContentType = photoContentType; } public String upload(){ //上传字段:上传到某个文件夹。存到应用的images目录下 String realPath = ServletActionContext.getServletContext().getRealPath("/images"); File directory = new File(realPath); if(!directory.exists()){ directory.mkdirs(); } try { for(int i=0;i<photo.length;i++){ FileUtils.copyFile(photo[i], new File(directory, photoFileName[i])); } return SUCCESS; } catch (IOException e) { e.printStackTrace(); return ERROR; } } } |
文件的下载
如下所示:下在一张指定位置的图片文件名为“女神张泓洋(22).jpg”
A.编写指定的Action类
package com.itheima.action; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.net.URLEncoder; import org.apache.commons.io.FilenameUtils; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class DownloadAction extends ActionSupport { private InputStream image;//用in有问题的 private String filename;//文件名 private long filesize; public InputStream getImage() { return image; } public void setImage(InputStream image) { this.image = image; } public String getFilename() { return filename; } public long getFilesize() { return filesize; } public String download() throws Exception{ //给image字节流赋值 String fileRealPath = ServletActionContext.getServletContext().getRealPath("/WEB-INF/classes/女神张泓洋 (22).jpg"); filename = FilenameUtils.getName(fileRealPath); //方式一:中文文件要进行URL编码 //filename = URLEncoder.encode(filename, "UTF-8"); filesize = new File(fileRealPath).length(); System.out.println(filename); image = new FileInputStream(fileRealPath); return SUCCESS; } } |
Struts.xml文件中的配置如下:
<constant name="struts.ognl.allowStaticMethodAccess" value="true" /> |
。。。。。。。。。ajijiiiaoj |
<action name="download" class="com.itheima.actions.DownloadAction" method="download"> <result type="stream"> <param name="inputName">image</param> <param name="contentType">application/octet-stream</param> <!-- 在struts.xml中使用OGNL表达式获取动作类中属性的值。 调用动作类中的 getFilename()--> <!-- 中文文件名编码:方式二.使用OGNL表达式,调用URLEncode的静态方法 --> <!-- 默认OGNL调用静态方法是不行的,需要开启一个常量开关.struts.ognl.allowStaticMethodAccess=true --> <param name="contentDisposition">attachment;filename=${@java.net.URLEncoder@encode(filename,'UTF-8')}</param> <param name="contentLength">${filesize}</param> </result> </action> |
strut2-学习笔记(二)的更多相关文章
-
WPF的Binding学习笔记(二)
原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...
-
AJax 学习笔记二(onreadystatechange的作用)
AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...
-
[Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计
源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...
-
JMX学习笔记(二)-Notification
Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...
-
java之jvm学习笔记二(类装载器的体系结构)
java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...
-
Java IO学习笔记二
Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...
-
《SQL必知必会》学习笔记二)
<SQL必知必会>学习笔记(二) 咱们接着上一篇的内容继续.这一篇主要回顾子查询,联合查询,复制表这三类内容. 上一部分基本上都是简单的Select查询,即从单个数据库表中检索数据的单条语 ...
-
NumPy学习笔记 二
NumPy学习笔记 二 <NumPy学习笔记>系列将记录学习NumPy过程中的动手笔记,前期的参考书是<Python数据分析基础教程 NumPy学习指南>第二版.<数学分 ...
-
Learning ROS for Robotics Programming Second Edition学习笔记(二) indigo tools
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
-
Redis学习笔记二 (BitMap算法分析与BitCount语法)
Redis学习笔记二 一.BitMap是什么 就是通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身.我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省 ...
随机推荐
-
SQL Server 2012提供的OFFSET/FETCH NEXT与Row_Number()对比测试 [T]
SQL Server 2008中SQL应用系列--目录索引 前些天看到一篇文章<SQL Server 2012 - Server side paging demo using OFFSET/FE ...
-
FireFox下上传控件的显示问题
Chrome正常 FireFox显示不正常 上传控件一直有个问题,就是样式问题,解决方法就是用一个大的背景层挡住,然后点大的背景层去触发上传控件的Click事件. Html: <span id= ...
-
CSS优先级算法是如何计算?
CSS的specificity特性或非凡性,它是一个衡量css优先级的一个标准, 既然的标准就有判定规定和计算方式,specificity用一个四位数来表示, 更像四级从左到右,左的最大级,一级大于一 ...
-
HDU1247 Hat’s Words(Trie的动态内存版本)
Hat’s Words Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Tota ...
-
Oracle单个数据文件超过32G后扩容
Oracle单个数据文件超过32G后扩容 表空间数据文件容量与DB_BLOCK_SIZE的设置有关,而这个参数在创建数据库实例的时候就已经指定.DB_BLOCK_SIZE参数可以设置为4K.8K. ...
-
linux环境变量配置总结
LD_LIBRARY_PATH: 动态库的查找路径设置:方法一: export LD_LIBRARY_PATH=LD_LIBRARY_PATH:/XXX 但是登出后就失效方法二: 修改~/.bash ...
-
HDU2138(Miller-Rabin素数检测)
最近在看RSA,找到一个一个大素数是好多加密算法的关键一步,而大素数无法直接构造,一般情况下都是生成一个随机数然后判断是不是素数.判断是否是素数的方法有好多,有的能够准确判断,比如可以直接因式分解(R ...
-
mssql sqlserver update delete表别名用法简介
转自:http://www.maomao365.com/?p=6973 摘要: 在sql脚本编写中,如果需要在update delete 中使用表别名的方法,必须按照一定的规则编写,否则将会出现相应 ...
-
【Netty】通俗地讲,Netty 能做什么?
作者:郭无心链接:https://www.zhihu.com/question/24322387/answer/78947405来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...
-
Visual studio 2017 Installer 打包.netframework
前几步和网上其他教程一样的.主要是把.net framework 打包进安装程序里,如果选的是“从与我的应用程序相同的位置下载系统必备组件”,会提示 ERROR: 要在“系统必备”对话框中启用“从与我 ...