话说:
亲爱的小美,前天的前天给你分享了Struts2的入门知识,今天就来点高级的。这篇博客本来打算作为10月的封笔之作,没想到到作为了11月的开篇之作。你准备好了么?
目录
一、基于通配符的DMI配置
二、存储值及OGNL
三、动态结果集实现用户登陆判断
四、实现用户的增删改查
五、文件上传
六、总结
开发环境:IntelliJ IDEA(2017.2.5)
整体架构:
整体效果图:
一、基于通配符的DMI配置
What DMI?
DMI——Dynamic Method Invocation动态方法调用
Why DMI?
没有DMI的时候,我们一个方法需要配置一个action,类似这样的:
<!--没有通配符,我们是以下这么配置的;你会发现action都相同-->
<!-- <action name="News_add" class="com.hmc.struts2.action.NewsAction">
</action>
<action name="News_update" class="com.hmc.struts2.action.NewsAction">
</action>
<action name="News_del" class="com.hmc.struts2.action.NewsAction">
</action>-->
<!--通配符可以实现action的简化,避免多个方法配置多个action-->
<!--News_update-->
<action name="*_*" class="com.hmc.struts2.action.{1}Action"
method="{2}">
<result name="success">/{1}/{2}.jsp</result>
</action>
<!--以上含义表示:页面访问News_update,会去Web目录下找News这个目录下的update.jsp,然后
指定NewsAction下面的update方法-->
这样就很麻烦,配置多个action,重复引用相同的class.就类似最开始我们写多个Servlet一样,每一个方法对应一个Servlet,之后我们用BaseServlet实现了一个Servlet中写多个方法。今天的Struts2也是遇到类似问题,就用DMI机制来处理。如何实现DMI呢?不再需要我们写什么BaseStruts2之类的,只需要配置。框架就是这样,操作简单了,配置麻烦了。
配置DMI 3步走:
第一步:配置action
第二步:查找default.properties,根据其中选项配置其他
第三步:配置动态调用
第一步:配置action
<!--通配符可以实现action的简化,避免多个方法配置多个action-->
<!--News_update-->
<action name="*_*" class="com.hmc.struts2.action.{1}Action"
method="{2}">
<result name="success">/{1}/{2}.jsp</result>
</action>
<!--以上含义表示:页面访问News_update,会去Web目录下找News这个目录下的update.jsp,然后
指定NewsAction下面的update方法-->
第二步:查找default.properties,根据其中选项配置其他
找default.properties有两种方式:
法一:下载的包里面找:
法二:依赖包里面找
发现里面有我们需要的各种配置信息,都可以参考这个配置:
比如:DynamicMethodInvocation、devMode、reload、multiPart.maxSize等等
第三步:配置动态调用
最核心的是要配置DynamicMethodInvocation和global-allowed-methods;其他配置都是为了更好开发而附加的。
<!--开启动态方法调动;这些都属于常量,所以用constant标签-->
<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
<!--struts.xml变动的时候,可以自动加载,不用重启猫-->
<constant name="struts.configuration.xml.reload" value="true"/>
<!--开启开发者模式,页面会显示详细错误,也会struts.xml也会自动加载-->
<constant name="struts.devMode" value="true"/>
//<package>里面配置:
<!--全局方法配置,这个方法写死了的-->
<!-- <global-allowed-methods>login</global-allowed-methods>-->
<!--为了更加的方便,所以用正则可以配置所有的方法-->
<global-allowed-methods>regex:.*</global-allowed-methods>
以上需要注意的是: global-allowed-methods在2.5版本以上的.dtd文档引用类型才支持,所以注意拷贝struts.xml的时候,注意下头部.dtd版本信息。否则找不到global-allowed-methods
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
以上是2.5版本,自己手动修改的。
好了,做到以上3步,就可以实现动态访问啦!最经典的动态访问方式是
http://localhost:8080/struts2_meice01/login!login
这里第一个login代表action名字,第二个login代表方法,如果这样可以访问,就代表动态方法完全配置成功!这样访问的时候,配置action是不需要加method属性的
<!--Action接受参数法一:属性方法-->
<action name="login" class="com.hmc.struts2.action.LoginAction" >
<result name="success">/login_success.jsp</result>
</action>
但是,因为我们要实现动态DMI,方法名和Action是有关联的,按照UserAction这样的规矩来命名的,所以就需要加上Method属性啦。UserAction表明:
当访问User_add的时候,直接去找UserAction这个类,然后调用该类中的add()方法。根据返回的类型,跳转到User文件夹下面的add.jsp,这样Action类、方法、页面三者就实现了完美的动态调用!
就不用request.getRequestDispatcher().forward(req,resp)这样跳转啦。
还需要注意的是,如果配置不到位,或者访问有误,会出现以下几种类型的错误:
1)Wrong method was defined as an action method: **——表明没有这个方法。要么是定义的实体类里没有方法;要么是有方法,但方法名不是execute,同时也没有配置method属性。
2)There is no Action mapped for namespace[/] or action name [ ] ——表明没有配置DMI权限,即没有配置:
name=”struts.enable.DynamicMethodInvocation”;如果已经配置了这个DMI权限,还报这个错误,表明没有配置全局方法;
3)* method is not allowed! ——这表明没有配置全局方法。
报错界面是以下这几种:
出现这个界面,如果你已经配置了DynamicMethodInvocation,表明还需要配置全局方法global-allowed-methods,所有满足正则表达式的方法都可以访问。
因为Struts2的Action实现方式有3种,默认实现的都是execute()方法。如果你要用自己的方法,就需要在action里面加上method属性指明方法名。去掉了method属性,又没有execute()方法(你用的是自己的方法),就会出现这个界面。
这个就表明没有配置global-allowed-methods
二、存储值及OGNL
OGNL ——Object-Graph Navigation Language 对象图导航语言
valueStack——值栈
ActionContext——Action的上下文
1)怎么查看valueStack呢?
非常简单,首先引入struts2标签
<%@ taglib uri="/struts-tags" prefix="s" %>
然后页面写上这个就可以
检验struts自带标签:/struts-tags
<s:debug/>
上面这个标签和语法就是OGNL;点击后,出现以下界面:
2)如何取值呢?在标签里面用#来取值
这里用struts2自带标签取值<br/>
<s:property value="#users"/><br/>
看可不可以取到key-session
<s:property value="#session"/>
为什么要用#取值呢,ONVL语法奥!
3)栈顶的值可以直接取值,而不用ActionContext.getContext().put();只有栈内容的值,才需要用#去取值。
还记得我们之前取值的3中方式吗?为什么我们在Action中定义属性只要和登录界面的name属性一致,访问Action的时候,就可以直接取到用户登陆信息呢?本质就是这个在起作用。
总结:
栈顶的值,都是不用#来取值的,但是如果不在栈顶,首先的需要用ActionContext.getContext().put()或者ActionContext.getSession().put放进去,然后通过#取出来。可是这个到底有啥用?这个就从你本质上表明了之前我们三种获取属性的方式,为什么页面的name属性在action里写对应的属性后,就可以直接取值,原因就是因为这些值在栈顶,可以直接取,如果不在栈顶,就需要在struts标签里用#取。如果你分不清属性在不在栈顶,就用struts-debug看一下。
三、动态结果集实现用户登陆判断
这里最核心的是动态结果集。之前我们跳转到的页面都是固定的,现在把跳转的界面变得灵活。
功能是这样的:用户登陆,存储登陆信息,判断是否是系统管理员,如果是,跳转到list.jsp页面,如果不是,提示信息,返回到login.jsp
LoginAction
package com.hmc.usermgr.action;
import com.hmc.usermgr.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ModelDriven;
/**
* User:Meice
* 2017/10/31
*/
public class LoginAction implements ModelDriven<User> {
private User user;
/**
* 用户登录
*/
public String login() {
//假设用户名:admin 密码:123
//成功后,把用户存到session中,然后跳转到首页
String url = "";
if("admin".equals(user.getUsername()) && "123".equals(user.getPassword())) {
ActionContext.getContext().getSession().put("username",user.getUsername());
url = "list.jsp";
}else {
//跳转到login.jsp 携带提示信息:用户名或密码不正确
ActionContext.getContext().put("msg","用户名或密码不正确~");
url = "login.jsp";
}
//存储URL,使用动态结果集 Dynamic Result Set
ActionContext.getContext().put("url",url);
return "success";
}
//实现方法;new对象
public User getModel() {
if(user == null) {
user = new User();
}
return user;
}
}
struts.xml
<!--配置LoginAction Login_login-->
<action name="login" class="com.hmc.usermgr.action.LoginAction" method="login">
<result name="success">${url}</result>
</action>
登陆界面及list界面这里省略。这样,当跳转页面需要判断的时候,我们就要考虑动态结果集啦。
四、实现用户的增删改查
1、建立UserAction
2、配置struts.xml
3、实体类User
4、页面
1、建立UserAction
package com.hmc.usermgr.action;
import com.hmc.usermgr.model.User;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ModelDriven;
import java.util.HashMap;
import java.util.Map;
/**
* User:Meice
* 2017/10/31
*/
public class UserAction implements ModelDriven<User> {
private User user;
//定义Map集合
private static Map<String,User> users = new HashMap<String,User>();
//list等是实体方法,必然要实例化对象UserAction,实体化对象必然实现了无参的构造方法
/* public UserAction() {
// System.out.println("初始化对象....");
User user1 = new User(1,"贾宝玉","宝玉哥哥",18,"123");
User user2 = new User(2,"林黛玉","黛玉妹纸",16,"321");
User user3 = new User(3,"史湘云","湘云女汉子",15,"1234");
User user4 = new User(4,"袭人","袭妹妹",16,"2345");
User user5 = new User(5,"平儿","平儿妹妹",14,"765");
//存到Map中
users.put(user1.getUsername(),user1);
users.put(user2.getUsername(),user2);
users.put(user3.getUsername(),user3);
users.put(user4.getUsername(),user4);
users.put(user5.getUsername(),user5);
}*/
static {
// System.out.println("初始化对象....");
User user1 = new User(1,"贾宝玉","宝玉哥哥",18,"123");
User user2 = new User(2,"林黛玉","黛玉妹纸",16,"321");
User user3 = new User(3,"史湘云","湘云女汉子",15,"1234");
User user4 = new User(4,"袭人","袭妹妹",16,"2345");
User user5 = new User(5,"平儿","平儿妹妹",14,"765");
//存到Map中
users.put(user1.getUsername(),user1);
users.put(user2.getUsername(),user2);
users.put(user3.getUsername(),user3);
users.put(user4.getUsername(),user4);
users.put(user5.getUsername(),user5);
}
//定义显示用户列表的方法
public String list() {
ActionContext.getContext().put("users",users);
return "success";
}
/**
* 此方法只负责页面跳转
*/
public String add() {
return "success";
}
//执行增加用户业务操作
public String doAdd() {
//为集合添加表单中的元素
users.put(user.getUsername(),user);
ActionContext.getContext().put("url","User_list");
return "redirectUrl";
}
/**
* 跳转到修改界面/WEB-INF/User/update.jsp
*/
public String update() {
//这里有个致命的bug,就是接收不到参数,百思不得其解....暂且搁置
User u = users.get(user.getUsername());
ActionContext.getContext().put("user",u);
return "success";
}
/**
* 执行修改方法
*/
public String doUpdate() {
//Map集合中key不能重复,所以修改就是替换
users.put(user.getUsername(),user);
ActionContext.getContext().put("url","User_list");
return "success";
}
/**
* 执行删除方法
*/
public String doDel() {
//这里也有致命的bug,就是接收不到参数,百思不得其解....暂且搁置
users.remove(user.getUsername());
ActionContext.getContext().put("url","User_list");
return "redirectUrl";
}
//实现方法
public User getModel() {
if(user == null) {
user = new User();
}
return user;
}
}
以上需要注意的主要有:
1)因为struts.xml中已经实现了动态方法调用,所以在写方法的时候,一个方法负责页面跳转,一个方法负责处理业务逻辑,处理业务逻辑一般用do**来命名;
2)页面跳转是否符合预期,主要是根据return来控制的。Struts2比较复杂的就是页面跳转;
3)在编辑及删除操作的时候,username是传过去了,但是ModelDriven里的user怎么都接收不到,百思不得解….,暂且搁置;
4)这里演示没有从后台拿数据,直接在静态语句块里面初始化了。注意在构造方法初始化与静态语句块初始化的区别:如果在构造方法里初始化,增加用户的时候,每次重复调用方法,Map集合都初始化,所以永远也增加不进去;只有静态语句块初始化一次,就可以增加进去。
2、配置struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!--开启动态方法调用 DMI-->
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<!--开启开发者模式-->
<constant name="struts.devMode" value="true"></constant>
<package name="usermger" extends="struts-default">
<!--开启全局方法 所有方法都允许动态方法调用-->
<global-allowed-methods>regex:.*</global-allowed-methods>
<!--配置LoginAction Login_login-->
<action name="login" class="com.hmc.usermgr.action.LoginAction" method="login">
<result name="success">${url}</result>
</action>
<action name="*_*" class="com.hmc.usermgr.action.{1}Action" method="{2}">
<!-- 规避list界面被轻易访问,放到WEB-INF下面,只有内部代码可以访问,手动是访问不了滴-->
<result name="success">/WEB-INF/{1}/{2}.jsp</result>
<result name="redirectUrl" type="redirect">${url}</result>
</action>
</package>
</struts>
这里和之前配置变化点:
1)页面全部放到了WEB-INF下,这样虽增加了页面跳转的难度,确保了页面访问更高安全系数。页面跳转就必须通过struts.xml来配置;
2)return可以设置多个,根据返回不同的情况,处理不同的方法。避免同一个操作需要用到2个方法的尴尬局面。
3) name类型、Type类型详情可以参考这篇文章。有的时候需要重定向,就需要指定Type,不然页面找不到。
3、实体类User
package com.hmc.usermgr.model;
/**
* User:Meice
* 2017/10/31
*/
public class User {
private int id;
private String username;
private String nickname;
private String password;
private int age;
public User() {}
public User(int id, String username, String nickname, int age,String password) {
this.id = id;
this.username = username;
this.nickname = nickname;
this.age = age;
this.password=password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", nickname='" + nickname + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
4、页面
list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<title>用户列表</title>
<body>
<h2></h2>
欢迎【${username}】登录本系统
<hr/>
<table border="1" width="80%" align="center">
<thead>
<tr>
<th>编号</th>
<th>用户名</th>
<th>昵称</th>
<th>密码</th>
<th>年龄</th>
<th>操作</th>
</tr>
</thead>
<tboday>
<c:forEach var="user" items="${users}">
<tr>
<td>${user.value.id}</td>
<td>${user.value.username}</td>
<td>${user.value.nickname}</td>
<td>${user.value.password}</td>
<td>${user.value.age}</td>
<td>
<a href="User_update?username=${user.value.username}">编辑</a>|
<a href="User_doDel?username=${user.value.username}" onclick="return confirm('真的忍心删除我麽?')">删除</a>
</td>
</tr>
</c:forEach>
</tboday>
</table>
<a href="User_add">新增</a>
</body>
</html>
add.jsp
<%--
User: Meice
Date: 2017/10/31
Time: 14:53
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>添加用户</title>
</head>
<body>
<h2>添加用户</h2>
<form action="User_doAdd" method="post">
<table>
<tr>
<td>用户名</td>
<td><input type="text" name="username"> </td>
</tr>
<tr>
<td>昵称</td>
<td><input type="text" name="nickname"></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td>年龄</td>
<td><input type="text" name="age"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="提交"/></td>
</tr>
</table>
</form>
</body>
</html>
update.jsp
<%--
User: Meice
Date: 2017/10/31
Time: 18:04
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>修改用户</title>
</head>
<body>
<h2>修改用户</h2>
<form action="User_doUpdate" method="post">
<table>
<tr>
<td>用户名</td>
<td><input type="text" name="username" value="${user.username}" readonly="readonly"> </td>
</tr>
<tr>
<td>昵称</td>
<td><input type="text" name="nickname" value="${user.nickname}"></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password" value="${user.password}"></td>
</tr>
<tr>
<td>年龄</td>
<td><input type="text" name="age" value="${user.age}"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="提交"/></td>
</tr>
</table>
</form>
</body>
</html>
页面没啥需要特别注意的。只不过这次用的是Map集合,username作为key,user对象作为value,取值的时候注意下。
另外,表单中的name要和实体类属性保持一致,ModelDriven才可以直接获取到。怎么获取到的呢?就是那个stackValue;同理,连接中的参数,这次ModelDriven没有获取到,很意外。
五、文件上传
1、写UploadAction
2、配置struts.xml
3、页面
回忆一下之前Servlet3.o中的文件上传主要用到的是:
1)给Servlet加注解
2)part对象。
1、写UploadAction
package com.hmc.usermgr.action;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
/**
* User:Meice
* 2017/11/1
*/
public class UploadAction {
/**
* 这是Struts2规定的方式
* File名和页面的name属性一致
* name属性+ContentType
* name属性+photoFileName
*/
private File photo;
private String photoContentType;
private String photoFileName;
public String upload() {
return "success";
}
public String doUpload() {
//输出看下
System.out.println(photo);//路径 这里photo仅仅只是个名字,上传任意附件都可以奥
System.out.println(photoContentType);//类型
System.out.println(photoFileName);//文件名
/**
* 文件路径 photo
* C:\Users\Administrator\.IntelliJIdea2017.2\system\tomcat\Unnamed_Meice_idea_utimate\work\Catalina\localhost\Struts2_meice02....
* image/jpeg 文件类型-photoContentTpye
* 001aa018f83f1510788c12.jpg 文件名-photoFileName
*/
//文件上传
try {
FileUtils.copyFile(photo,new File("F:\\"+photoFileName));
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
public File getPhoto() {
return photo;
}
public void setPhoto(File photo) {
this.photo = photo;
}
public String getPhotoContentType() {
return photoContentType;
}
public void setPhotoContentType(String photoContentType) {
this.photoContentType = photoContentType;
}
public String getPhotoFileName() {
return photoFileName;
}
public void setPhotoFileName(String photoFileName) {
this.photoFileName = photoFileName;
}
}
注意:
1)3个属性photo、photoContentType、 photoFileName是框架规定必须这么写的,所以要定义。各自对应关系详见注释;要生成ge()、set()方法,才可以获取到;
2)最核心的就是FileUtils.copyFile(文件名;要输出的路径);
3) 上传文件默认配置大小是:default.properties里面有:
# uses javax.servlet.context.tempdir by default
struts.multipart.saveDir=
struts.multipart.maxSize=2097152
默认才好可怜的2M,也就是说,你上传的文件如果大小超过2M,页面直接报错。
如果想要随心所欲的上传文件,就手动在struts.xml中配置即可。
<!--修改上传文件大小-->
<constant name="struts.multipart.maxSize" value="2097152000"/>
2、配置struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!--开启动态方法调用 DMI-->
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<!--开启开发者模式-->
<constant name="struts.devMode" value="true"></constant>
<!--修改上传文件大小-->
<constant name="struts.multipart.maxSize" value="2097152000"/>
<package name="usermger" extends="struts-default">
<!--开启全局方法 所有方法都允许动态方法调用-->
<global-allowed-methods>regex:.*</global-allowed-methods>
<!--配置LoginAction Login_login-->
<action name="login" class="com.hmc.usermgr.action.LoginAction" method="login">
<result name="success">${url}</result>
</action>
<action name="*_*" class="com.hmc.usermgr.action.{1}Action" method="{2}">
<!-- 规避list界面被轻易访问,放到WEB-INF下面,只有内部代码可以访问,手动是访问不了滴-->
<result name="success">/WEB-INF/{1}/{2}.jsp</result>
<result name="redirectUrl" type="redirect">${url}</result>
</action>
</package>
</struts>
其他和之前一样,增加上传文件大小配置struts.multipart.maxSize。
3、页面
upload.jsp
<%--
User: Meice
Date: 2017/11/1
Time: 8:31
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>文件上传</h2><br>
<hr/>
<br/>
<form action="Upload_doUpload" method="post" enctype="multipart/form-data" >
<table>
<tr>
<td><input type="file" name="photo" value="请选择上传文件" /></td>
</tr>
<tr>
<td><input type="submit" value="保存" /></td>
</tr>
</table>
</form>
</body>
这里注意加上enctype=”multipart/form-data”属性即可。
再次提醒以下,这次我们把页面全部放到了WEB-INF目录下,直接访问是访问不到的,必须通过struts.xml配置才能访问奥。
如果要上传多个文件呢?So easy! 使用数组就可以啦。
UploadAction
package com.hmc.usermgr.action;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
/**
* User:Meice
* 2017/11/1
*/
public class UploadAction {
/**
* 这是Struts2规定的方式
* File名photo和页面的name属性一致
* name属性+ContentType
* name属性+photoFileName
*/
private File photo[];
private String photoContentType[];
private String photoFileName[];
public String upload() {
return "success";
}
public String doUpload() {
//输出看下
System.out.println(photo);//路径 这里photo仅仅只是个名字,上传任意附件都可以奥
System.out.println(photoContentType);//类型
System.out.println(photoFileName);//文件名
/**
* 文件路径 photo
* C:\Users\Administrator\.IntelliJIdea2017.2\system\tomcat\Unnamed_Meice_idea_utimate\work\Catalina\localhost\Struts2_meice02....
* image/jpeg 文件类型-photoContentTpye
* 001aa018f83f1510788c12.jpg 文件名-photoFileName
*/
//文件上传
try {
/**
* 多文件的好处是,都不用考虑Null的情况,直接后台处理好了
*/
for(int i =0;i<photo.length;i++) {
FileUtils.copyFile(photo[i],new File("F:\\"+photoFileName[i]));
}
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
public File[] getPhoto() {
return photo;
}
public void setPhoto(File[] photo) {
this.photo = photo;
}
public String[] getPhotoContentType() {
return photoContentType;
}
public void setPhotoContentType(String[] photoContentType) {
this.photoContentType = photoContentType;
}
public String[] getPhotoFileName() {
return photoFileName;
}
public void setPhotoFileName(String[] photoFileName) {
this.photoFileName = photoFileName;
}
}
变化点在于把变量变成了数组,界面增加几个上传文件即可。
upload.jsp
<%--
User: Meice
Date: 2017/11/1
Time: 8:31
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>文件上传</h2><br>
<hr/>
<br/>
<form action="Upload_doUpload" method="post" enctype="multipart/form-data" >
<table>
<tr>
<td><input type="file" name="photo" value="请选择上传文件" /></td>
<td><input type="file" name="photo" value="上传文件"></td>
<td><input type="file" name="photo" value="上传文件"></td>
</tr>
<tr>
<td><input type="submit" value="保存" /></td>
</tr>
</table>
</form>
</body>
</html>
六、总结
1、为简化action配置,使用DMI。使用通配符配置action,配置dynamicMethodInvocation及global-allowed-methods即可。
2、Struts2自带有便签,可以通过#取值。这里的语言就是OGNL。在valueStack中,栈顶的值可以直接取到,非栈顶的值需要在sturts标签里用#取值。这里深刻表明了之前3中接受参数的方式本质;
3、使用动态结果集可以让我们返回的界面更加灵活;把返回的界面用变量代替,然后通过ActionContext.getContext().put()传过去即可;
4、Struts2实现CURD比较方便,不用每次接收参数之类的,但是要注意页面的跳转和方法命名技巧。方法中一个方法负责页面跳转,一个方法负责处理业务逻辑。返回通过配置多个result来实现。这里没有实现UD功能,很遗憾,因为username传到页面了,但是ModelDriven中的user接收不到,暂且搁置;
5、文件上传核心是如果你的文件路径是X,那么要在Action里面定义三个属性(x,xContentType,xFileName)并且生成get(),set(),这样页面提交就可以获取到上传文件全部信息。同时调用FileUtiles.copyFile()方法,既可以输出到制定位置。默认文件大小2M,我们可以通过配置文件修改。