话说:

亲爱的小美,前天的前天给你分享了Struts2的入门知识,今天就来点高级的。这篇博客本来打算作为10月的封笔之作,没想到到作为了11月的开篇之作。你准备好了么?

目录


一、基于通配符的DMI配置
二、存储值及OGNL
三、动态结果集实现用户登陆判断
四、实现用户的增删改查
五、文件上传
六、总结


开发环境:IntelliJ IDEA(2017.2.5)
整体架构:

不同版本emoji_User

整体效果图:

不同版本emoji_取值_02

一、基于通配符的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有两种方式:

法一:下载的包里面找:

不同版本emoji_不同版本emoji_03

法二:依赖包里面找

不同版本emoji_User_04

发现里面有我们需要的各种配置信息,都可以参考这个配置:
比如: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! ——这表明没有配置全局方法。


报错界面是以下这几种:

不同版本emoji_struts_05

出现这个界面,如果你已经配置了DynamicMethodInvocation,表明还需要配置全局方法global-allowed-methods,所有满足正则表达式的方法都可以访问。

不同版本emoji_User_06

因为Struts2的Action实现方式有3种,默认实现的都是execute()方法。如果你要用自己的方法,就需要在action里面加上method属性指明方法名。去掉了method属性,又没有execute()方法(你用的是自己的方法),就会出现这个界面。

不同版本emoji_取值_07

这个就表明没有配置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;点击后,出现以下界面:

不同版本emoji_不同版本emoji_08

2)如何取值呢?在标签里面用#来取值

这里用struts2自带标签取值<br/>
<s:property value="#users"/><br/>

看可不可以取到key-session
<s:property value="#session"/>

为什么要用#取值呢,ONVL语法奥!

不同版本emoji_不同版本emoji_09

3)栈顶的值可以直接取值,而不用ActionContext.getContext().put();只有栈内容的值,才需要用#去取值。

还记得我们之前取值的3中方式吗?为什么我们在Action中定义属性只要和登录界面的name属性一致,访问Action的时候,就可以直接取到用户登陆信息呢?本质就是这个在起作用。

不同版本emoji_User_10

总结:

栈顶的值,都是不用#来取值的,但是如果不在栈顶,首先的需要用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配置才能访问奥。

不同版本emoji_不同版本emoji_11

如果要上传多个文件呢?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,我们可以通过配置文件修改。