Java-Struts学习笔记之拦截器、文件上传下载、xml、json

文件结构
文件结构
文件结构
文件结构

Struts2应用的分层体系架构 要点:
1.将处理逻辑放到service里,如Action层不准许出现在sql语句,session、request不允许传到service层去。

示例:
LoginAction.java

package com.struts2.struts2;
……该导入的包……
public class LoginAction extends ActionSupport
{
    private String username;
    private String password;
    private int age;
    private Date date;
    private LoginService loginService = new LoginServiceImpl();
    ……Set与get方法……
    public String execute() throws Exception
    {    //业务逻辑不写到Action里
        //        if(!"hello".equals(username))
        //        {
        //            throw new UsernameException("username invalid");
        //        }
        //        if(!"world".equals(password))
        //        {
        //            throw new PasswordException("password invalid");
        //        }
        //        return SUCCESS;
        //利用ServletActionContext,获得HttpServletRequest请求
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpSession session = request.getSession();
        session.setAttribute("hello", "helloworld");
        ActionContext actionContext = ActionContext.getContext();
        Map<String, Object> map = actionContext.getSession();    //获取session
        Object object = map.get("hello");    //打印出session的信息
        System.out.println(object);
        if(this.loginService.isLogin(username, password))
        {
            return SUCCESS;
        }
        return INPUT;
    }
………………
}

LoginService.java

package com.struts2.service;
public interface LoginService
{
    public boolean isLogin(String username, String password);
}

LoginServiceImpl.java

package com.struts2.service.impl;
import com.struts2.service.LoginService;
public class LoginServiceImpl implements LoginService
{
    public boolean isLogin(String username, String password)
    {
        if("hello".equals(username) && "world".equals(password))
        {
            return true;
        }
        return false;
    }
}

编辑struts2.xml文件

…………
<action name="login" class="com.struts2.struts2.LoginAction">
    <result name="success" type="dispatcher">/result.jsp</result>
    <result name="input">/login.jsp</result>
</action>
…………

Struts2的模型驱动(model driven)

要点:
1.属性驱动(propertity driven)与模型驱动的区别:之前的都是属性驱动,属性驱动与模型驱动的处理方式差不多,但它们导致的结果却不同,属性驱动是在接收的请求后,将对象里的值一个一个的 set 好,此时并没有new 出一个整的对象。而模型驱动是在接收到用户提交表单请求后,就将对象创建好了,并把对象的值也设定好了,可以直接拿对象来用了。
2.实现了ModelDriven接口的Action的执行过程

1).首先调用getModel()方法,返回一个person对象
2).模型查找提交过来的表单中有没有username、password等属性名是否匹配,如果匹配,则将对应的值自动设置到对象中,放到模型当中。
3).当在结果页面,以request.getAttribute()或requestScope取值时,同样通过Action从模型当中取值。

属性驱动与模型驱动的比较
1)属性驱动灵活,准确;模型驱动不灵活,因为很多时候,页面所提交过来的参数并不属于模型中的属性,也就是说页面所提交过来的参数与模型中的属性并不一致,这是很常见的情况。
2)模型驱动更加符合面向对象的编程风格,使得我们获得的是对象而不是一个个离散的值。
小结:推荐使用属性驱动编写 Action。
Preparable接口的作用是让 Action完成一些初始化工作,这些初始化工作是放在 Preparable接口的prepare方法中完成的,该方法会在execute方法执行之前得到调用。

示例:
Person.java

package com.struts2.bean;
import java.util.Date;
public class Person
{
    private String username;
    private String password;
    private int age;
    private Date date;
    ……getset方法……
}

LoginAction2.java

package com.struts2.struts2;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import com.struts2.bean.Person;
public class LoginAction2 extends ActionSupport implements ModelDriven<Person>, Preparable
{    //模型驱动
    private Person person = new Person();
    //1.首先调用getModel()方法,返回一个person对象
    //2.模型查找提交过来的表单中有没有username、password等属性值,如果有,则模型自动设置到对象中
    public Person getModel()
    {
        System.out.println("getModel invoked!");
        return person;    //返回生成的person
    }
    //调用这个方法让action准备自身
    public void prepare() throws Exception
    {
        System.out.println("prepare invoked!!");
    }
    public String execute() throws Exception
    {
        System.out.println("execute invoked!");
        //System.out.println(person.getUsername());
        return SUCCESS;
    }
}

struts.xml
向struts.xml文件中,添加一个action,如下:

<action name="login2" class="com.struts2.struts2.LoginAction2">
    <result name="success">/result.jsp</result>
</action>

login.jsp

…………
<form action="login2.action">
    username: <input type="text" name="username"><br>
    password: <input type="password" name="password"><br>
    age: <input type="text" name="age"><br>
    date: <input type="text" name="date"><br>
    <input type="submit" value="submit">
</form>
 …………

result.jsp

…………
<body>
    username: ${requestScope.username }<br><!-- requestScope相当于request.getAttribute() -->
    password: ${requestScope.password }<br>
    age: ${requestScope.age }<br>
    date: ${requestScope.date }<br>
    session: ${sessionScope.hello }<!-- 取得通过ServletActionContext方式获取的session -->
</body>
…………

服务器端代码的单元测试

要点:
服务器端代码的单元测试有两种模式
1) 容器内测试(Jetty)
2) Mock 测试 ( 继承 HttpServletRequest 、 HttpSession 、HttpServletResponse等Servlet API) 。

防止表单重复提交

要点:
1. 采取请求转发的方式完成表单内容的添加会造成内容的重复插入。采取重定向的方式实现数据的添加不会导致数据的重复插入(即:重定向到一个list查询Action)。
2. 因为struts.xml文件中<package name="struts2" extends="struts-default">,所以继续了struts-default中的结果类型。

防止表单重复提交的两种方式
1) 通过重定向
2) 通过Session Token(Session令牌):当客户端请求页面时,服务器会通过token标签生成一个随机数,并且将该随机数放置到 session当中,然后将该随机数发向户端;如果客户第一次提交,那么会将该随机数发往服务器端,服务器会接收到该随机数并且与 session 中所保存的随机数进行比较,这时两者的值是相同的,服务器认为是第一次提交,并且将更新服务器端的这个随机数值;如果此时再次重复提交,那么客户端发向服务器端的随机数还是之前的那个,而服务器端的随机数则已经发生了变化,两者不同,服务器就认为这是重复提交,进而转向invalid.token所指向的结果页面。

Struts-default.xml中定义的结果类型
打开struts2-core-2.2.1.1.jar下的struts-default.xml文件,其中定义了的结果类型,如下:

<package name="struts-default" abstract="true">
<result-types>
    <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
    <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
    <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
    <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
    <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
    <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
    <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
    <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
    <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
    <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
</result-types>
………………

页面重定向与请求转发的设定
在struts.xml文件中,设置Action中的result元素的type属性即可,如下:

<action name="login" class="com.struts2.struts2.LoginAction">
    <!--
    <result name="success" type="dispatcher">/result.jsp</result>
-->
    <result name="success" type="redirect">/result.jsp</result>
    <result name="input">/login.jsp</result>
</action>

重定向到Action (redirectAction)
action1.jsp

…………
<body>
<form action="action1.action">
    username: <input type="text" name="username"><br>
    password: <input type="password" name="password"><br>
    <input type="submit" value="submit">
</form>
</body>
…………

action2.jsp

…………
<body>
username: <s:property value="username"/><br>
password: <s:property value="password"/><br>
usernameAndPassword: <s:property value="usernameAndPassword"/>
</body>
…………

Action1.java

package com.struts2.struts2;
import com.opensymphony.xwork2.ActionSupport;
public class Action1 extends ActionSupport
{
    private String username;
    private String password;
    private String usernameAndPassword;
    ……setget方法……
    @Override
    public String execute() throws Exception
    {
        this.usernameAndPassword = this.username + this.password;
        return SUCCESS;
    }
}

Action2.java

package com.struts2.struts2;
import com.opensymphony.xwork2.ActionSupport;
public class Action2 extends ActionSupport
{
    private String username;
    private String password;
    private String usernameAndPassword;
    ………getset方法………
    @Override
    public String execute() throws Exception
    {
        return SUCCESS;
    }
}

struts.xml

…………
<action name="action1" class="com.struts2.struts2.Action1">
    <result name="success" type="redirectAction">
        <!--如果不传参数,可把action2直接写到result的text域中 -->
        <param name="actionName">action2</param>
        <param name="username">${username}</param><!-- 获取Action1的成员变量 -->
        <param name="password">${password}</param>
        <param name="usernameAndPassword">${usernameAndPassword}</param>
    </result>
</action>
<action name="action2" class="com.struts2.struts2.Action2">
<result name="success">/action2.jsp</result>
</action>
…………

chain结果类型(Action之间的请求转发)
要点:直接修改struts.xml文件中的action的result的type属性的值为 chain ,如下:

…………
<action name="action1" class="com.vvvv.struts2.Action1">
    <result name="success" type=chain">
                <!--如果不传参数,可把action2直接写到result的text域中 -->
    <param name="actionName">action2</param>
    <param name="username">${username}</param><!-- 获取Action1的成员变量 -->
    <param name="password">${password}</param>
    <param name="usernameAndPassword">${usernameAndPassword}</param>
</result>
 …………

运行可以发现,这种方式不是以重定向的形式转发,且action2.jsp页面拿不到usernameAndPassword的信息。因为由Action1转发到到Action2,是在服务器内部进行了,是在一个客户端页面(action1.jsp)请求范围内,而请求中只包含username、password两个参数。所以拿不到usernameAndPassword的值。

Struts2 Session Token机制
token.jsp

…………
<body>
    <s:form action="token.action" theme="simple">
        username: <s:textfield name="username"></s:textfield><br>
        password: <s:password name="password"></s:password><br>
        <s:token></s:token>
        <s:submit value="submit"></s:submit>
    </s:form>
</body>
…………

TokenAction.java

package com.struts2.struts2;
import com.opensymphony.xwork2.ActionSupport;
public class TokenAction extends ActionSupport
{
    private String username;
    private String password;
    ……getset方法……
    @Override
    public String execute() throws Exception
    {
        return SUCCESS;
    }
}   

tokenSuccess.jsp

…………
<body>
username: <s:property value="username"/><br>
password: <s:property value="password"/>
</body>
…………

tokenFail.jsp

…………
<body>
不要重复提交表单
</body>
…………

struts.xml

…………
<action name="token" class="com.struts2.struts2.TokenAction">
    <result name="success">/tokenSuccess.jsp</result>
    <result name="invalid.token">/tokenFail.jsp</result>
    <interceptor-ref name="token"></interceptor-ref>
    <interceptor-ref name="defaultStack"></interceptor-ref>
</action>
…………

struts2拦截器

要点:  
拦截器(Interceptor) :拦截器是Struts2的核心,Struts2的众多功能都是通过拦截器来实现的;
拦截器在服务器被启动时,被初始化,调用init方法;

拦截器的配置  
1)编写实现Interceptor 接口的类。
2)在struts.xml 文件中定义拦截器。
3)在action中使用拦截器

首先编写拦截器类(TheInterceptor1)

package com.struts2.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class TheInterceptor1 implements Interceptor
{
    private String test;
    public String getTest()
    {
        return test;
    }
    public void setTest(String test)
    {
        System.out.println("setTest invoked!");    //set方法先于init方法执行
        this.test = test;
    }
    public void destroy()
    {
    }
    public void init()
    {
        System.out.println("init invoked!");
        System.out.println("test: " + this.test);
    }
    //服务器永远不会去执行挂截器,只不过是在执行拦截器之前,执行一个invoke方法
    public String intercept(ActionInvocation invocation) throws Exception
    {
        System.out.println("before");
        String result = invocation.invoke();
        System.out.println("after");
        return result;
    }
}

struts.xml

<struts>
    <package name="struts2" extends="struts-default">
        <interceptors>
            <interceptor name="theInterceptor1" class="com.vvvv.interceptor.TheInterceptor1">
                <param name="test">vvvv</param>
            </interceptor>
        </interceptors>
        ………………
        <action name="token" class="com.struts2.struts2.TokenAction">
            <result name="success">/tokenSuccess.jsp</result>
            <result name="invalid.token">/tokenFail.jsp</result>
            <interceptor-ref name="token"></interceptor-ref>
            <interceptor-ref name="defaultStack"></interceptor-ref>
        </action>
    </package>
</struts>

一旦定义了自己的拦截器,将其配置到 action上后,我们需要在action的最后加上默认的拦截器栈:defaultStack。

示例2
项目源包 : struts2sd

要点:

  1. 定义拦截器时可以直接继承AbstractInterceptor 抽象类(该类实现了 Interceptor 接口,并且对 init 和 destroy 方法进行了空实现) ,然后实现其抽象方法 intercept即可;
  2. 方法过滤拦截器(可以对指定方法进行拦截的拦截器);
  3. 在方法过滤拦截器中,如果既没有指定 includeMethods 参数,也没有指定execludeMethods 参数,那么所有的方法都会被拦截,也就是说所有的方法都被认为是 includeMethods 的;如果仅仅指定了 includeMethods,那么只会拦截 includeMethods 中的方法,没有包含在includeMethods 中的方法就不会被拦截。

TheInterceptor2.java

package com.vvvv.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class TheInterceptor2 extends AbstractInterceptor
{
    @Override
    public String intercept(ActionInvocation invocation) throws Exception
    {
        System.out.println("interceptor before...");
        System.out.println("interceptor2: " + invocation.getAction().getClass());
        String result = invocation.invoke();
        System.out.println("interceptor after...");
        return result;
    }
}

struts.xml

…………
<interceptors>
    <interceptor name="theInterceptor1" class="com.vvvv.interceptor.TheInterceptor1">
        <param name="test">vvvv</param>
    </interceptor>
    <interceptor name="theInterceptor2" class="com.vvvv.interceptor.TheInterceptor2">
    </interceptor>
    <interceptor name="theInterceptor3" class="com.vvvv.interceptor.TheInterceptor3">
    </interceptor>
    <interceptor name="loginInterceptor" class="com.vvvv.interceptor.LoginInterceptor">
    </interceptor>
</interceptors>
        …………
<action name="action1" class="com.vvvv.struts2.Action1" method="myExecute">
<result name="success" type="chain">
    <param name="actionName">action2</param>
    <param name="username">${username}</param>
    <param name="password">${password}</param>
    <param name="usernameAndPassword">${usernameAndPassword}</param>
</result>
<interceptor-ref name="theInterceptor1"></interceptor-ref>
<interceptor-ref name="theInterceptor2"></interceptor-ref>
<!-- 方法过滤拦截器 -->
<interceptor-ref name="theInterceptor3">
    <param name="includeMethods">execute, myExecute</param>
    <!-- 不包含以下拦截器,使其不执行 --><!--
        <param name="excludeMethods">myExecute</param>
         -->
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
…………

TheInterceptor3

package com.vvvv.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import com.vvvv.listener.TheListener;
public class TheInterceptor3 extends MethodFilterInterceptor
{
    @Override
    protected String doIntercept(ActionInvocation invocation) throws Exception
    {
        invocation.addPreResultListener(new TheListener());
        System.out.println("before interceptor3");
        String result = invocation.invoke();
        System.out.println("after interceptor3");
        return result;
    }
}

示例3 struts.xml

<interceptors>
    <interceptor name="theInterceptor1" class="com.vvvv.interceptor.TheInterceptor1">
        <param name="test">vvvv</param>
    </interceptor>
    <interceptor name="theInterceptor2" class="com.vvvv.interceptor.TheInterceptor2">
    </interceptor>
    <interceptor name="theInterceptor3" class="com.vvvv.interceptor.TheInterceptor3">
    </interceptor>
    <!-- 配置login拦截器 -->
    <interceptor name="loginInterceptor" class="com.vvvv.interceptor.LoginInterceptor">
    </interceptor>
    <interceptor-stack name="myDefaultInterceptorStack">
        <interceptor-ref name="loginInterceptor"></interceptor-ref>
        <interceptor-ref name="defaultStack"></interceptor-ref>
    </interceptor-stack>
</interceptors>
<default-interceptor-ref name="myDefaultInterceptorStack"></default-interceptor-ref>
<global-results>
<result name="usernameInvalid">/usernameInvalid.jsp</result>
<result name="passwordInvalid">/passwordInvalid.jsp</result>
<result name="login">/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="usernameInvalid" exception="com.vvvv.exception.UsernameException"></exception-mapping>
<exception-mapping result="passwordInvalid" exception="com.vvvv.exception.PasswordException"></exception-mapping>
</global-exception-mappings>
<action name="login" class="com.vvvv.struts2.LoginAction">
<result name="success" type="dispatcher">/result.jsp</result>
<result name="input">/login.jsp</result>
</action>
<action name="login2" class="com.vvvv.struts2.LoginAction2">
<result name="success">/result.jsp</result>
</action>
<action name="userAction" class="com.vvvv.struts2.UserAction">
<result name="success">/output.jsp</result>
</action>
<action name="userAction2" class="com.vvvv.struts2.UserAction2">
<result name="success">/output.jsp</result>
</action>
<action name="register" class="com.vvvv.struts2.RegisterAction" method="myExecute">
<result name="success">/registerResult.jsp</result>
<result name="input">/register.jsp</result>
</action>
<action name="action1" class="com.vvvv.struts2.Action1" method="myExecute">
<result name="success" type="chain">
    <param name="actionName">action2</param>
    <param name="username">${username}</param>
    <param name="password">${password}</param>
    <param name="usernameAndPassword">${usernameAndPassword}</param>
</result>
…………

LoginInterceptor.java

package com.vvvv.interceptor;
import java.util.Map;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.vvvv.struts2.LoginAction;
public class LoginInterceptor extends AbstractInterceptor
{
    @Override
    @SuppressWarnings("unchecked")
    public String intercept(ActionInvocation invocation) throws Exception
    {
        if(LoginAction.class == invocation.getAction().getClass())
        {
            return invocation.invoke();
        }
        Map map = invocation.getInvocationContext().getSession();
        if(null == map.get("userInfo"))
        {
            return Action.LOGIN;
        }
        return invocation.invoke();
    }
}

同时,也可以在一个Struts.xml中有多个package ,例如,可以将前台的Action放到一个package里,将后台的一个package放到另一个包里面。

也可以将配置文件拆分开来,写到多个配置文件中,例如:

<struts>
    <include file="xxx.xml"></include>
    …………

struts-2.3.1-all.zip\struts-2.3.1\apps\struts2-mailreader.war\WEB-INF\src\java\struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <constant name="struts.action.extension" value="do" />
    <constant name="struts.devMode" value="false" />
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <include file="mailreader-default.xml"/>
    <include file="mailreader-support.xml"/>
</struts>

struts命名空间

  1. package 元素的 abstract 属性表示该包是抽象的,不能直接使用,需要由子包继承才可以使用。struts-default 这个 package 就是abstract的,因此需要我们继承这个包来使用。
  2. package元素的 namespace属性起到命名空间分割的作用。 通常将namespace的属性值定义成页面所在的目录名。

配置struts.xml

…………
<package name="hello" extends="struts-default" namespace="/theNamespace">
    <action name="namespaceAction" class="com.vvvv.struts2.NamespaceAction">
        <result name="success">output.jsp</result>
    </action>
</package>
…………

NamespaceAction(命名空间)

package com.vvvv.struts2;
import com.opensymphony.xwork2.ActionSupport;
public class NamespaceAction extends ActionSupport
{
    @Override
    public String execute() throws Exception
    {
        return SUCCESS;
    }
}

实现文件上传

要点:  
1. 进行文件上传时,必须将表单的 method属性设为post,将enctype属性设为multipart/form-data。  
2. Struts2在进行文件上传操作时,实际上是通过两个步骤实现的:  

1)首先将客户端上传的文件保存到struts.multipart.saveDir键所指定的目录中,如果该键所对应的目录不存在,那么就保存到javax.servlet.context.tempdir 环境变量所指定的目录中。
2)Action中所定义的File类型的成员变量file实际上指向的是临时目录中的临时文件,然后在服务器端通过 IO的方式将临时文件写入到指定的服务器端目录中。

示例1 fileUpload.jsp

<form action="fileUpload.action" method="post" enctype="multipart/form-data">
    username: <input type="text" name="username"><br>
    file: <input type="file" name="file"><br>
    <input type="submit" value="submit">
</form>

fileUploadResult.jsp

<body>
username: <s:property value="username"/><br>
name:<s:property value="fileFileName"/>
<br>
type:<s:property value="fileContentType"/>
</body>

通过servlet实现对文件的上传
UploadServlet.java

package com.vvvv.servlet;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet
{
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        //DiskFileItemFactory创建在内存中保存内容的FileItem实例
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //得到真实绝对路径
        String path = req.getRealPath("/upload");
        //把工厂的仓库设置为真实路径
        factory.setRepository(new File(path));
        //设置阀值
        factory.setSizeThreshold(1024 * 1024);
        //创建ServletFileUpload实例
        ServletFileUpload upload = new ServletFileUpload(factory);
        try
        {
            //解析HttpServletRequest到List到中
            List<FileItem> list = (List<FileItem>)upload.parseRequest(req);
            for(FileItem item : list)
            {
                //获得表单字段的名字
                String name = item.getFieldName();
                //判断是否是表单字段
                //如果是普通文本域返回true; 如果不是普通文本域返回false.
                if(item.isFormField())
                {
                    //获得表单字段的值
                    String value = item.getString();
                    System.out.println(name + "=" + value);
                    req.setAttribute(name, value);
                }
                else
                {
                    String value = item.getName();
                    int start = value.lastIndexOf("\\");
                    String fileName = value.substring(start + 1);
                    req.setAttribute(name, fileName);
                    item.write(new File(path, fileName));
//                    OutputStream os = new FileOutputStream(new File(path, fileName));
//                    InputStream is = item.getInputStream();
//                    byte[] buffer = new byte[400];
//                    int length = 0;
//                    while((length = is.read(buffer)) != -1)
//                    {
//                        os.write(buffer, 0, length);
//                    }
//                    is.close();
//                    os.close();
                }
            }
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
        req.getRequestDispatcher("fileUploadResult.jsp").forward(req, resp);
    }
}

Struts2的配置文件
WEB-INF/lib/struts2-core-2.2.1.1.jar/org.apache.struts2/default.properties

…………
### This can be used to set your default locale and encoding scheme
# struts.locale=en_US
struts.i18n.encoding=UTF-8        //指定了国际化的默认编码格式
### if specified, the default object factory can be overridden here
### Note: short-hand notation is supported in some cases, such as "spring"
### Alternatively, you can provide a com.opensymphony.xwork2.ObjectFactory subclass name here
# struts.objectFactory = spring
### specifies the autoWiring logic when using the SpringObjectFactory.
### valid values are: name, type, auto, and constructor (name is the default)
struts.objectFactory.spring.autoWire = name
…………
### Used by the DefaultActionMapper
### You may provide a comma separated list, e.g. struts.action.extension=action,jnlp,do
### The blank extension allows you to match directory listings as well as pure action names
### without interfering with static resources.
struts.action.extension=action,,    //指定了表单提交的文件的后缀名
…………

单个文件上传
fileUpload.jsp

…………
<form action="fileUpload.action" method="post" enctype="multipart/form-data">
    username: <input type="text" name="username"><br>
    file: <input type="file" name="file"><br>
    <input type="submit" value="submit">
</form>
…………

fileUploadResult.jsp

…………
<body>
    username: <s:property value="username"/><br>
    name:<s:property value="fileFileName"/>
    <br>
    type:<s:property value="fileContentType"/>
</body>
…………

UploadAction.java

package com.vvvv.struts2;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UploadAction extends ActionSupport
{
    private String username;
    private File file;
    private String fileFileName;
    private String fileContentType;
    …………setget方法…………
    @Override
    public String execute() throws Exception
    {
        //文件上传的步骤是:先将上传的文件缓存在struts.multipart.saveDir=xxx指定的目录下,
        //然后从xxx目录下,通过输出流将文件上传到服务器指定的目录("/upload")
        String root = ServletActionContext.getRequest().getRealPath("/upload");
        InputStream is = new FileInputStream(file);
        System.out.println("path: " + file.getAbsolutePath());
        System.out.println("file: " + file.getName());
        System.out.println("fileFileName: " + fileFileName);
        File destFile = new File(root, fileFileName);
        OutputStream os = new FileOutputStream(destFile);
        byte[] buffer = new byte[400];
        int length = 0;
        while(-1 != (length = is.read(buffer)))
        {
            os.write(buffer, 0, length);
            Thread.sleep(1000);
        }
        is.close();
        os.close();
        return SUCCESS;
    }
}

struts.properties

#定义文件的拓展名
struts.action.extension=action
#指定上传文件的缓存目录
struts.multipart.saveDir=c:/vvvv
#限定上传文件的最大大小
struts.multipart.maxSize=1048576000

注:限定上传文件的大小等也可以在struts.xml文件中配置,如下:

<struts><!-- 限定2M -->
    <constant name="struts.multipart.maxSize" value="5242880"></constant>
    …………
</struts>

多个文件上传 fileUpload2.jsp

<form action="fileUpload2.action" method="post" enctype="multipart/form-data">
    username: <input type="text" name="username"><br>
    file: <input type="file" name="file"><br>
    file2: <input type="file" name="file"><br>
    file3: <input type="file" name="file"><br>
    <input type="submit" value="submit">
</form>

fileUploadResult2.jsp

<body>
username: <s:property value="username"/><br>
<s:iterator value="fileFileName" id="f">
    file: <s:property value="#f.toUpperCase()"/><br><!-- ognl表达示 -->
</s:iterator>
</body>

UploadAction2.java

package com.vvvv.struts2;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UploadAction2 extends ActionSupport
{
    private String username;
    private List<File> file;
    private List<String> fileFileName;
    private List<String> fileContentType;
    …………setget方法…………
    @Override
    public String execute() throws Exception
    {
        for(int i = 0; i < file.size(); i++)
        {
            InputStream is = new FileInputStream(file.get(i));
            String root = ServletActionContext.getRequest().getRealPath("/upload");
            File destFile = new File(root, fileFileName.get(i));
            OutputStream os = new FileOutputStream(destFile);
            byte[] buffer = new byte[400];
            int length = 0;
            while(-1 != (length = is.read(buffer)))
            {
                os.write(buffer, 0, length);
            }
            is.close();
            os.close();
        }
        return SUCCESS;
    }
}

struts2文件下载
struts.xml

<package>
    <!--…………-->
    <action name="fileUpload2" class="com.vvvv.struts2.UploadAction2">
        <result name="success">/fileUploadResult2.jsp</result>
    </action>
    <action name="downloadFile2" class="com.vvvv.struts2.DownloadAction2">
        <result type="stream"><!-- 动态的获得文件的名字 -->
            <param name="contentDisposition">attachment;filename=${filename}</param>
            <param name="inputName">downloadFile</param>
        </result>
    </action>
</package>

downloadFile.jsp

<body>
    <a href="downloadFile2.action?number=1">下载文件</a>
</body>

downloadAction2.java

package com.vvvv.struts2;
import java.io.InputStream;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class DownloadAction2 extends ActionSupport
{
    private int number;
    private String filename;
    …………get、set方法…………
    public InputStream getDownloadFile()
    {
        try
        {
            if (1 == number)
            {
                //动态的修改文件的名字
                this.filename = "中文.txt";
                //转码
                this.filename = new String(this.filename.getBytes("gbk"),
                        "8859_1");
                return ServletActionContext.getServletContext()
                        .getResourceAsStream("/upload/中文.txt");
            }
            else
            {
                this.filename = "CaptureSprite.exe";
                return ServletActionContext.getServletContext()
                        .getResourceAsStream("/upload/CaptureSprite.exe");
            }
        }
        catch (Exception ex)
        {
        }
        return null;
    }
    @Override
    public String execute() throws Exception
    {
        return SUCCESS;
    }
}

基于注解的配置

要点
在项目中引入名为:struts2-convention-plugin-2.2.1.1.jar的jar 包,并删除掉struts.xml
打开struts2-convention-plugi-xxx.jar的结构如下:

文件结构

编辑LoginAction.java,

package com.vvvv.struts2;
@ParentPackage("struts-default")
@Action(value = "login", results = {
        @Result(name = "success", location = "/login.jsp"),
        @Result(name = "input", location = "/login.jsp") })
//@InterceptorRef("defaultStack")
//@InterceptorRefs({@InterceptorRef(""), @InterceptorRef("")})
//@ExceptionMappings({@ExceptionMapping(),@E.....})
public class LoginAction extends ActionSupport
{
    private String username;
    private String password;
    private int age;
    private Date date;
    private LoginService loginService = new LoginServi
    …………
}

Struts2异步处理xml
项目文件结构
文件结构

要点: 向项目中导入dom4j.jar包

GetXMLAction.java

package com.vvvv.action.xml;
…………getset方法…………
public class GetXMLAction extends ActionSupport
{
    private String name;
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    @Override
    public String execute() throws Exception
    {
        //zhang san
        People people1 = new People();
        people1.setId(1);
        people1.setName("zhangsan");
        people1.setAge(30);
        people1.setAddress("beijing");
        People people2 = new People();
        people2.setId(2);
        people2.setName("lisi");
        people2.setAge(50);
        people2.setAddress("tianjin");
        //创建文档对象
        Document document = DocumentHelper.createDocument();
        //创建根元素
        Element rootElement = document.addElement("persons");
        rootElement.addComment("This is comment!!");
        Element e = rootElement.addElement("person");
        Element idElement = e.addElement("id");
        Element nameElement = e.addElement("name");
        Element ageElement = e.addElement("age");
        Element addressElement = e.addElement("address");
        if("zhangsan".equals(name))
        {
            idElement.setText(people1.getId() + "");
            nameElement.setText(people1.getName());
            ageElement.setText(people1.getAge() + "");
            addressElement.setText(people1.getAddress());
        }
        else{
            idElement.setText(people2.getId() + "");
            nameElement.setText(people2.getName());
            ageElement.setText(people2.getAge() + "");
            addressElement.setText(people2.getAddress());
        }
        //创建ajax响应
        HttpServletResponse response = ServletActionContext.getResponse();
        response.setContentType("text/xml; charset=utf-8");
        response.setHeader("cache-control", "no-cache");
        PrintWriter out = response.getWriter();
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("utf-8");
        XMLWriter writer = new XMLWriter(out, format);
        writer.write(document);
        out.flush();
        out.close();
        return null;
    }
}

People.java

package com.vvvv.action.xml;
public class People
{
    private int id;
    private String name;
    private int age;
    private String address;
    …………getset方法…………
}

Struts2异步处理Json
要点
向项目中导入Json插件,struts2-json-plugin-xxx.jar

getJson.jsp

<script type="text/javascript" src="scripts/jquery-1.4.4.js"></script>
<script type="text/javascript">
    $(function()
    {
        $("#button1").click(function()
        {
            $.post("getJsonAction2.action",{name: $("#name").val()},
                    function(returnedData, status)
                    {
                        var people = returnedData;
                        var id = people.id;
                        var name = people.name;
                        var age = people.myAge;
                        var address = people.address;
                        var html = "<table width='60%' border='1' align='center'><tr><th>id</th><th>name</th><th>age</th><th>address</th><tr align='center'><td>" + id + "</td><td>" + name + "</td><td>" + age + "</td><td>" + address + "</td></tr></table>";
                        $("#theBody table:eq(0)").remove();
                        $("#theBody").append(html);
                    });
        });
    });
</script>
</head>
<body id="theBody">
<select id="name">
    <option value="zhangsan">zhangsan</option>
    <option value="lisi">lisi</option>
</select>
<input type="button" value="get json content from server" id="button1">
</body>

GetJsonAction.java

package com.vvvv.action.json;
…………getset方法…………
public class GetJsonAction extends ActionSupport
{
    private String name;
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    @Override
    public String execute() throws Exception
    {
        People people = new People();
        people.setId(1);
        people.setName(name);
        people.setAge(30);
        people.setAddress("beijing");
        Gson gson = new Gson();
        String result = gson.toJson(people);
        HttpServletResponse response = ServletActionContext.getResponse();
        response.setContentType("application/json; charset=utf-8");
        response.setHeader("cache-control", "no-cache");
        PrintWriter out = response.getWriter();
        out.print(result);
        out.flush();
        out.close();
        return null;
    }
}

GetJsonAction2.java(利用struts2的插件)

package com.vvvv.action.json;
public class GetJsonAction2 extends ActionSupport
{
    private String name;
    private int id;
    private int age;
    private String address;
    …………getset方法…………
    @Override
    public String execute() throws Exception
    {
        this.id = 1;
        this.age = 30;
        this.address = "beijing";
        return SUCCESS;
    }
}

注:本文档参考张龙老师的struts2教程

版权所有,转载请注明出处 luowei.github.io.