超全面 struts2 复习总结笔记

原创版权申明:本文章从本人 csdn 博客转到简书。
如有转载,请申明:转载自 IT天宇http://www.jianshu.com/p/cf2d189c45bf

前言

What ? 最近怎么开始写后端的博客了?


从去年开始就经常看到别人提“移动开发寒冬”,而年初投简历的时候更是亲身体会,不写3年经验连面试机会都没有,那么没有经验或经验少的人能怎么办呢,从一开就找不到工作怎么可能会有经验?


然而绝望并没有用,我算运气好,勉强找到一份工作。

从趋势来看,近几年移动开发待遇不会很好,今年发现到处缺后端,于是决定还是搞后端吧,因为搞安卓不过一年,后端的知识还没忘完(好吧,实际上以前后端也没多深入),于是利用下班时间复习后端(好吧,其实好多是预习了●﹏●),并把知识整理出来分享给大家。

目录

  1. 环境搭建
  2. Action
  3. 配置详解
  4. 通配符和动态方法调用
  5. OGNL
  6. Struts 标签
  7. Validator
  8. Interceptor
  9. 国际化
  10. 常见例子

正文

<a id="1"> </a>

1.环境搭建

导包

  • 版本管理工具导入

    • Gradle
      compile "org.apache.struts:struts2-core:2.5.10.1"
      
    • Maven
      <dependency>
        <groupId>org.apache.struts</groupId>
        <artifactId>struts2-core</artifactId>
        <version>2.5.10.1</version>
      </dependency>
      

    上面的仅仅是核心包,根据需要可引入其他包,比如注解包

    compile "org.apache.struts:struts2-convention-plugin:2.5.10"
    

配置

web.xml

struts2 基于拦截器,因此,配置的第一步是在 WEB-INF/web.xml 中配置 struts2 拦截器。

配置的时候需要注意,不同的版本的 StrutsPrepareAndExecuteFilter 包路径不一样,配置的时候,可以用 IED 搜索这个类,复制路径。比如 Idea 的搜索类快捷键是 Ctrl + N

  • 2.5.x 版本

    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

为了防止篇幅过大,其他版本就不写了。

struts.xml

在 src 目录下建立 struts.xml,Idea 中需要放到 resources 下。

<?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>

</struts>

<a id="2"> </a>

2.Action

HelloAction

编写一个类继承 ActionSupport, 并提供一个无参返回字符串且公开的方法,如下:

public class HelloAction extends ActionSupport {
    public String hello() throws Exception {
        System.out.println("hello world!");
        return SUCCESS;
    }
}

配置 Action

类似于 Servlet,struts2 使用 action 来处理请求。
使用 action 需要进行配置。

xml 配置

在 struts.xml 的 struts 标签里面配置 action
如下:

<package name="p1" extends="struts-default">
    <action name="hello" class="com.ittianyu.web.action.HelloAction" method="login">
        <result name="success">hello.jsp</result>
    </action>
</package>

action 外面是 package,类似于 java 的包。
这里简单介绍 action 的属性,后面配置详解里会一一介绍大部分标签和属性。

name: 访问 action 的名称,如果没有配置 namespace,则 根路径/name 就是 action 的访问地址。
class 和 method 则是指定 action 的类和方法。

注解配置

使用注解前必须保证 已经导入了 struts2-convention-plugin 的包。

在 Action 类上加上 @ParentPackage("struts-default")
在对应的方法上加上

@Action(value = "hello", results = {
           @Result(name = "success", location="/hello.jsp")})

如下:

@ParentPackage("struts-default")
public class HelloAction extends ActionSupport {
    @Action(value = "hello", results = {
               @Result(name = "success", location="/hello.jsp")})
    public String hello() throws Exception {
        System.out.println("hello world!");
        return SUCCESS;
    }
}

配置好后,就可以通过浏览器访问(根路径取决于项目配置)
http://127.0.0.1/strut2/hello.action
如果访问成功,控制台后打印 hello world!

向 Action 传递参数

登录是很常见的 action,这个时候一般要向服务器传递 username, password 等。

User

创建一个 User 实体对象,假设里面只有 username, password。

public class User {
    private String username;
    private String password;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

UserAction

创建一个 UserAction 来处理请求,简单起见,使用注解方式配置。

@ParentPackage("struts-default")
public class UserAction extends ActionSupport implements ModelDriven<User>{
    private User user = new User();
    @Action(value = "login", results = {
               @Result(name = "success", location="/home.jsp")})
    public String login() throws Exception {
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        return SUCCESS;
    }
    @Override
    public User getModel() {
        return user;
    }
}

实现 ModelDriven 方法,返回 user。
然后在执行 login 方法之前,ModelDriven 拦截器会给 user 设置请求提交的值。

获取 Servlet Api

使用 ServletActionContext
比如:

ServletActionContext.getRequest();
ServletActionContext.getResponse();

<a id="3"> </a>

3.配置详解

include

用于引入其他 struts2 配置文件
比如:

<struts>
    <include file="struts-part1.xml"/>
    <include file="struts-part2.xml"/>
</struts>

package

|名称 |类型 |默认 |必须 |描述
|----|-----|-----|----|-----|---
|name |string |无 |yes |包的名称
|extends |string |无 |no |这个包所继承的父包名称.一般继承 struts-default
|namespace |string |/ |no |包的命名空间,如果配置了,在访问时要加上包名
|abstract |boolean |false |no |是否是抽象包,如果是,专门设计来被继承,一般配置了自己的拦截器栈时,会设计基础包。

action

|名称 |类型 |默认 |必须 |描述
|----|-----|-----|----|-----|---
|name |string |无 |yes |动作的名称
|class |string |com.opensymphony.xwork2.ActionSupport |no |绑定的动作类。若不配置,则为默认返回 success 的动作类。
|method |string |execute |no |动作绑定的方法
|converter |string |无 |no |动作的类型转换器

result

<result> 是 <action>的一个子元素,用于指定 action 的结果视图。

|名称 |类型 |默认 |必须 |描述
|----|-----|-----|----|-----|---
|name |string |success |no |对应动作方法的返回值
|type |string |dispatcher |no |结果类型。struts定义的结果类型有10种。

type 类型

|序号 |结果类型 |取值 |描述
|----|-----|-----|----|-----
|1 |Chain Result |chain |转发方式转到一个antion
|2 |Dispatcher Result |dispatcher | 转发到一个页面
|3 |FreeMarker Result |freemarker |
|4 |HttpHeader Result |httpheader |
|5 |Redirect Result |redirect | 重定向到一个页面
|6 |Redirect Action Result |redirectAction |重定向转到一个action
|7 |Stream Result |stream | 输出流,一般用于下载
|8 |Velocity Result |velocity |
|9 |XSL Result |xslt |
|10 |PlainText Result |plainText |常用于显示个别页面的源码

global-results

包内全局的结果视图,包内 action 没有配置 result 时,会使用全局结果视图。一般是在基础包里面配置,比如全局的登录验证。

interceptors

用于声明拦截器。

在 interceptors 中配置 interceptor, interceptor-stack,interceptor-ref 等声明拦截器。

default-interceptor-ref

用于配置包的默认拦截器。

constant

用于配置 struts2 中的常量。

<a id="4"> </a>

4.通配符和动态方法调用

通配符

struts2 中 action 的 name 支持通配符 *
用于匹配0个或多个字符。
旧版中可以直接使用,在新版中,需要启用通配符。
在 package 开头加上

<!--动态方法配置,2.5之后额外配置允许的方法名-->
<global-allowed-methods>regex:.*</global-allowed-methods>

此外还可以通过 {n} 来引用 匹配的值。

<action name="*_*" class="com.ittianyu.javaeetest.web.action.{2}Action" method="{1}{2}">
    <result name="success">/{1}{2}.jsp</result>
</action>

如上配置,我们访问 hello_World 时,会自动匹配方法 helloWorld,并在方法执行完成后,转发到 helloWorld.jsp

动态方法调用

struts2 支持动态指定 action所绑定的方法名,也就是不需要配置 action 的 method。
在请求中使用 !分割,后面写调用的方法名。
比如:

http://127.0.0.1/struts2/user!add

默认是关闭的,要使用需要配置。在 struts 标签下加上:

<constant name="struts.enable.DynamicMethodInvocation" value="true"/>

<a id="5"> </a>

5.OGNL

OGNL 是 Object Graph Navigation Language (对象图形导航语言)的缩写。
主要用来访问 栈 和 map 中的对象。

  • 支持对象方法调用,如 xxx.doSomeSpecial();
  • 支持类静态的方法调用和值访问,表达式的格式:
    @[类全名]@[方法名 | 值名],例如:
    @java.lang.String@format('foo %s', 'bar') 或
    @tutorial.MyConstant@APP_NAME;
    
    但需要设置 struts.ognl.allowStaticMethodAccess=true
  • 支持赋值操作和表达式串联,如 price=100, discount=0.8,
    price*discount,这个表达式会返回80;
  • 访问OGNL上下文(OGNL context)和ActionContext;
  • 操作集合对象。

在 jsp 中使用 OGNL 需要引入 struts tag lib。

<%@ taglib prefix="s" uri="/struts-tags" %>

然后使用 标签 s:property:

<s:property value="'hello world'"/>

使用引号引起来表示字符串,否则就是 OGNL 表达式

ValueStack

ValueStack 实际是一个接口,在 Struts2 中利用OGNL时,实际上使用的是该接口实现类 OgnlValueStack。

每个 Action 类的对象实例都有一个 ValueStack 对象,用于保存参数或结果。

在 ValueStack 对象的内部有两个逻辑部分:

  • ObjectStack: Struts 把动作和相关对象压入 ObjectStack 中,看作 List 容器。
  • ContextMap: Struts 把各种各样的映射关系存入 ContextMap 中,看作 Map 容器。

Struts2 会把下面这些对象存入 ContextMap 中

  • parameters: 该 Map 中包含当前请求的请求参数
  • request: 该 Map 中包含当前 request 对象中的所有属性
  • session: 该 Map 中包含当前 session 对象中的所有属性
  • application:该 Map 中包含当前 application 对象中的所有属性
  • attr: 该 Map 按如下顺序来检索某个属性: request, session, application

访问 ValueStack

  • ObjectStack: 直接通过元素的名称进行访问。
  • ContextMap: 需要在开头加一个 #

比如 ObjectStack 和 ContextMap 中都存放了一个 User,而 User
有属性 username,那么我们可以通过如下代码进行访问:

jsp 中:

<%--不加修饰符是在栈中查找对象是否有getUsername方法
可以通过[n].来指定从第几个开始查找,n从0开始
如果没找到,就会报错
--%>
<s:property value="username" />

<%--加修饰符# 是在 map 中查找 key 为 user 的 值
如果存在 session 中,则访问路径为 #session.user.username
--%>
<s:property value="#user.username" />

Action 中:

ValueStack vs = ActionContext.getContext().getValueStack();
// setValue 方法的第一个参数是 OGNL 表达式,不加#表示放到栈中
vs.setValue("name", "Mike");// 在栈中查找是否有对象有 setName 方法
vs.setValue("#name", "Mike");// 往 map 中存入 key 为 name,值为 Mike 数据。

// set 方法是对 栈 进行操作
// 如果栈顶是 map,则把数据放入map,否则在栈顶新建一个 map,并存入数据。
vs.set("username", "Jane");

# $ % 的使用

  • 1、取 contextMap 中 key 时使用,例如 <s:property value="#name" />
    2、OGNL 中创建Map对象时使用,例如: <s:radio list="#{'male':'男','female':'女'}" />
  • $
    1、在JSP中使用EL表达式时使用,例如 ${name}
    2、在xml配置文件中,编写OGNL表达式时使用,例如文件下载时,文件名编码。
    struts.xml——>${@java.net.URLEncoder.encode(filename)}
    
  • %
    在struts2中,有些标签的value属性取值就是一个OGNL表达式,例如 <s:property value="OGNL Expression" />
    还有一部分标签,value属性的取值就是普通字 符串,例如 <s:textfield value="username"/>,如果想把一个普通的字符串强制看成时OGNL,就需要使用 %{} 把字符串套起来。
    例如 <s:textfield value="%{username}"/>。当然在 <s:property value="%{OGNL Expression}" /> 也可以使用,但不会这么用。

投影

  • “?#”:过滤所有符合条件的集合,如:users.{?#this.age > 19};
  • #”:过滤第一个符合条件的元素,如:users.{#this.age > 19};
  • “$#”:过滤最后一个符合条件的元素,如:users.{$#this.age > 19}

<a id="6"> </a>

6.Struts 标签

表单标签

常见的表单标签在 struts2 中都有

比如: form,textfield,password,checkbox,checkboxlist ,hidden,submit,reset 等。

<s:form action="register">
    <s:textfield name="username" label="用户名" requiredLabel="true" requiredPosition="left"/>
    <s:password name="password" label="密码"/>
    <s:textfield name="birthday" label="生日"/>
    <s:checkbox name="married" value="true" label="已婚"/>
    <s:checkboxlist list="{'吃饭','睡觉','写代码'}" name="hobby" label="爱好" />
    <s:select list="#{'BJ':'北京','SH':'上海','SZ':'深圳'}" label="城市" headerKey="" headerValue="---请选择城市---"/>
    <s:radio name="gender" list="#{'male':'男','female':'女'}" label="性别" value="'male'"/>
    <s:textarea label="描述" rows="5" cols="30"/>
    <s:submit value="注册" />
    <s:reset value="重置" />
</s:form>

set

<%-- set 属性详解
    value: ognl 表达式,会存入 map 中
    var:作为存入 map 数据的 key
    scope:指定存入的是哪个 map,有 application,session,request,page,action
 --%>

<%-- 并不会往 map 中存入任何值,因为 value 被解释为 OGNL 之后是非法的,所以结果是空 --%>
<s:set value="Mike" var="name" scope="session"/>

<%--  加上单引号之后,解释为字符串,存到 session 中 --%>
<s:set value="'Mike'" var="name2" scope="session"/>

<%-- 不写 scope 默认存到 request 和 contextMap 中了 --%>
<s:set value="'Mike'" var="name3"/>

action

<%-- action 属性详解 
    name: action 名称
    executeResult: 是否执行 action
一般用于调用其他 action 并在页面上显示结果
--%>
<s:action name=="action1" executeResult="true">

if elseif else

<%--存入一个成绩--%>
<s:set var="grade" value="90"/>
<s:if test="#grade<60">差</s:if>
<s:elseif test="#grade<80">中</s:elseif>
<s:else>优秀</s:else>

url a

<%-- s:url 属性详解 
    value: 输出的 value 值。不是 OGNL 表达式
    action: action 名称
    var: 如果写了,就会把 action 的地址存到 contextMap 中,var 作为 key。
--%>

<%-- 指定 value 直接在页面输出 value的值 --%>
<s:url value="哈哈哈"/> <br/>

<%-- 指定 action 在页面输出 action 的完整地址 --%>
<s:url action="addUser1"/> <br/>

<%-- 指定了 var 则存到 map 中 --%>
<s:url action="addUser1" var="url">
    <%--可定义参数,相当于get表单的地址一样--%>
    <s:param name="name" value="'张三'"/>
</s:url>

a 标签和 url 类似

<a id="7"> </a>

7.Validator

struts2为我们共内置了16个验证器,且全部是基于字段的验证器。

required

验证字段的值是不是 null。注意,不是空字符串或空白字符串。

<validators>
    <field name="password">
        <field-validator type="required">
            <message>The password field is required!</message>
        </field-validator>
    </field>
</validators>

requiredstring

验证字段的值既不是null、也不是空白。

参数:

  • fieldName:要验证的字段名
  • trim:是否去掉首尾空格
<validators>
    <field name="userName">
        <field-validator type="requiredstring">
            <message>Please input the userName!</message>
        </field-validator>
    </field>
    <field name="password">
        <field-validator type="requiredstring">
            <param name="trim">false</param>
            <message>Please input the password!</message>
        </field-validator>
    </field>
</validators>

int

验证某个字段的值是否可以被转换为一个整数。还可以验证是否在允许的范围内。

参数:

  • fieldName:要验证的字段名
  • min:允许的最小值
  • max:允许的最大值

基于字段的验证

<validators>
    <field name="age">
        <field-validator type="int">
            <param name="min">18</param>
            <param name="max">60</param>
            <message>The age must be between ${min} and ${max}</message>
        </field-validator>
    </field>
</validators>

基于验证器的验证

<validators>
    <validator type="int">
        <param name="fieldName">age</param>
        <param name="min">18</param>
        <param name="max">60</param>
        <message>The age must be between ${min} and ${max}</message>
    </validator>
</validators>

long short

同 int

double

用来验证某个字段的值是否可以被转换为一个双精度浮点数。还可验证是否在允许的范围内。

参数:

  • fieldName:要验证的字段名
  • minInclusive:允许的最小值,包含最小值
  • maxInclusive:允许的最大值,包含最大值
  • minExclusive:允许的最小值,不包含最小值
  • maxExclusive:允许的最大值,不包含最大值
<validators>
    <field name="percentage1">
        <field-validator type="double">
            <param name="minInclusive">20.1</param>
            <param name="maxInclusive">50.1</param>
            <message> The age must be between ${ minInclusive } and ${ maxInclusive }(含)</message>
        </field-validator>
    </field>
    <field name="percentage2">
        <field-validator type="double">
            <param name="minExclusive">0.345</param>
            <param name="maxExclusive">99.987</param>
            <message> The age must be between ${ minExclusive } and ${ maxExclusive }(不含)</message>
        </field-validator>
    </field>
</validators>

date

用来确保给定的日期字段的值在指定的范围内。

参数:

  • fieldName:要验证的字段名
  • min:允许的最小值,包含最小值
  • max:允许的最大值,包含最大值
<validators>
    <field name="birthday">
        <field-validator type="date">
            <param name="min">2011-01-01</param>
            <param name="max">2011-12-31</param>
            <message>日期必须为2011年</message>
        </field-validator>
    </field>
</validators>

expression

用于验证是否满足一个OGNL表达式。这是一个非字段的验证。只有给定的参数的返回值是true时才能验证通过。验证不通过时产生一个动作错误,因此要显示该错误,需要使用<s:actionerror/>标签。

<validators>
    <validator type="expression">
        <param name="expression">
            maxNumber>minNumber
        </param>
        <message>最大值必须大于最小值</message>
    </validator>
</validators>

field expression

用于验证某个字段是否满足一个OGNL表达式。这是一个基于字段的验证。只有给定的参数的返回值是true时才能验证通过。验证不通过时产生一个字段错误。

参数:

  • fieldName:要验证的字段名
  • expression:OGNL表达式,只有该表达式为true才能验证通过
<validators>
    <field name="maxNumber">
        <field-validator type="fieldexpression">
            <param name="expression">
            maxNumber>100
        </param>
        <message>最大值必须大于最小值1</message>
        </field-validator>
    </field>
</validators>

email

用来验证给定的字段是否符合一个Email的规范。它的正则表达式为

\\b(^[_A-Za-z0-9-](\\.[_A-Za-z0-9-])*@([A-Za-z0-9-])+((\\.com)|(\\.net)|(\\.org)|(\\.info)|(\\.edu)|(\\.mil)|(\\.gov)|(\\.biz)|(\\.ws)|(\\.us)|(\\.tv)|(\\.cc)|(\\.aero)|(\\.arpa)|(\\.coop)|(\\.int)|(\\.jobs)|(\\.museum)|(\\.name)|(\\.pro)|(\\.travel)|(\\.nato)|(\\..{2,3})|(\\..{2,3}\\..{2,3}))$)\\b
<validators>
    <field name="email">
        <field-validator type="email">
            <message>请输入正确的邮箱</message>
        </field-validator>
    </field>
</validators>

url

用来验证给定的字段值是否是一个合法的URL地址。

<validators>
    <field name="url">
        <field-validator type="url">
            <message>请输入正确的地址</message>
        </field-validator>
    </field>
</validators>

visitor

该验证程序可以提高代码的可重用性,你可以利用它把同一个验证程序配置文件用于多个动作。

<validators>
    <field name="streetName">
        <field-validator type="requiredstring">
            <message>请输入正确街道地址</message>
        </field-validator>
    </field>
</validators>

<validators>
    <field name="address">
        <field-validator type="visitor">
            <message>Address:</message>
        </field-validator>
    </field>
</validators>

stringlength

用来验证一个非空的字段值是不是有足够的长度。

regex

用来检查给定字段是否与给定的正则表达式相匹配。正则表达式的详细内容可以参考 JDK 的 java.util.regex.Pattern 类。

参数:

  • fieldname:要验证的字段名
  • expression:正则表达式
  • caseSensitive:是否区分大小写的情况,默认 true
  • trim:是否去掉首尾空格,默认 true
<validators>
    <field name="userName">
        <field-validator type="regex">
            <param name="expression"><![CDATA[([aAbBcCdD][123][eEfFgG][456])]]></param>
            <message> 用户名必须符合规范</message>
        </field-validator>
    </field>
</validators>

<a id="8"> </a>

8.Interceptor

Struts2 拦截器在访问某个 Action 方法之前或之后实施拦截, Struts2 拦截器是可插拔的, 拦截器是 AOP 的一种实现.

常用拦截器

  • conversionError:将错误从ActionContext中添加到Action的属性字段中。
  • fileUpload:提供文件上传功能
  • i18n:记录用户选择的locale
  • model-driven:如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。
  • params:将请求中的参数设置到Action中去。
  • servletConfig:提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问。
  • token:避免重复提交
  • validation:使用 action-validation.xml 文件中定义的内容校验提交的数据。
  • workflow:调用 Action 的 validate 方法,一旦有错误返回,重新定位到 INPUT 视图

自定义拦截器

  1. 自定义拦截器
    public class PermissionInterceptor implements Interceptor {
       private static final long serialVersionUID = -5178310397732210602L;
       public void destroy() {
       }
       public void init() {
       }
       public String intercept(ActionInvocation invocation) throws Exception {
        System.out.println("进入拦截器");    
        if(session里存在用户){
            String result = invocation.invoke();
        }else{
            return “logon”;
        }
        //System.out.println("返回值:"+ result);
        //return result;
        }
    }
    
  2. 在 struts.xml 文件中配置自定义的拦截器
    <package name="itcast" namespace="/test" extends="struts-default">
        <interceptors>
                 <interceptor name=“permission" class="cn.itcast.aop.PermissionInterceptor" />
                 <interceptor-stack name="permissionStack">
             <interceptor-ref name="defaultStack" />
            <interceptor-ref name=" permission " />
                  </interceptor-stack>
        </interceptors>
        <action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">
            <result name="success">/WEB-INF/page/hello.jsp</result>
            <interceptor-ref name="permissionStack"/>
        </action>
    </package>
    

<a id="9"> </a>

9.国际化

struts2 中使用的 properties 文件来做国际化

配置资源包

  • 全局资源包
<!--
在struts.xml 中配置常量指定全局字符串包位置
-->
<constant name="struts.custom.i18n.resources" value="com.ittianyu.i18n.strings" />
  • 包范围的资源包:把 资源包放在某包下面,命名为:package_语言代码_国家代码.properties
  • 局部消息资源包:把 资源包放在某动作类路径下,命名为:动作类名称_语言代码_国家代码.properties

资源包的使用顺序:局部 > 包范围 > 全局

读取资源包

  • Action
    public class I18nAction extends ActionSupport {
        public String execute() {
            String value = getText("key");
        }
    }
    
  • jsp
    <s:text name="key"/>
    

手动指定读取的资源包

当注定的包下没有找到指定的值时,会按顺序搜索配置了的资源包

<s:i18n name="com.xxxx.package">
    <s:text name="key"/>
</s:i18n>

<a id="10"> </a>

10.常见例子

防重复提交

  1. 使用重定向,避免刷新就提交

        <result type="redirect">/success.jsp</result>
    
  2. 使用 tokensession 或 token 拦截器
    表单中加入 <s:token />

        <s:form action="login">
            <s:token />
        </s:form>
    

    配置中加入 tokensession 拦截器

    <action name="login" class="xxxx">
        <interceptor-ref name="defaultStack" />
        <interceptor-ref name="tokensession" />
        <result type="redirect">/success.jsp</result>
    </action>
    

    token 和 tokensession 功能一样,但有一些差别。
    token 拦截到重复提交后,会转向 invalid.token 结果视图
    而 tokensession 拦截之后不转向任何视图

上传

html

<s:form action="upload.action" enctype="multipart/form-data">
    <s:textfield name="username" label="用户名"/>
    <s:file name="photo" label="照片"/>
    <s:submit value="上传"/>
</s:form>

action

public class UploadAction extends ActionSupport{
    public String username;
    public File photo;
    public String photoFileName;// 上传文件名。变量命名规格 字段名+FileName
    public String photoContentType;// 上传文件的MIME类型。变量命名规格 字段名+ContentType

    public String upload() {
        // 获取文件存储目录
        ServletContext servletContext = ServletActionContext.getServletContext();
        String path = servletContext.getRealPath("/WEB-INF/files");
        File file = new File(path);
        if (!file.exists())
            file.mkdirs();
        // 存储到目标路径
        photo.renameTo(new File(file, photoFileName));
        return NONE;
    }
}

修改上传文件大小限制

<!--限制上传最大尺寸为 1048576 byte-->
    <constant name="struts.multipart.maxSize" value="1048576"/>

限制上传文件扩展名

<action name="upload" class="com.ittianyu.javaeetest.web.action.UploadAction" method="upload">
    <interceptor-ref name="defaultStack">
        <param name="fileUpload.allowedExtensions">jpg,png</param>
    </interceptor-ref>
    <result name="input">upload.jsp</result>
</action>

多文件上传需要把

public File photo;
public String photoFileName;// 上传文件名。变量命名规格 字段名+FileName
public String photoContentType;// 上传文件的MIME类型。变量命名规格 字段名+ContentType

改成,也就是改成数组

public File[] photo;
public String[] photoFileName;// 上传文件名。变量命名规格 字段名+FileName
public String[] photoContentType;// 上传文件的MIME类型。变量命名规格 字段名+ContentType

下载

action

public class DownloadAction extends ActionSupport {
    public InputStream inputStream;
    public String filename;

    public String download() throws Exception{
        // 找到文件路径
        String path = ServletActionContext.getServletContext().getRealPath("/WEB-INF/files/1.jpg");
        // 包装成流
        inputStream = new FileInputStream(path);
        // 设置浏览器接收时文件名
        filename = "图片.jpg";

        return SUCCESS;
    }
}

配置

<action name="download" class="com.ittianyu.javaeetest.web.action.DownloadAction" method="download">
    <result name="success" type="stream">
        <!--下载类型为 bin-->
        <param name="contentType">application/octet-stream</param>
        <!--下载打开方式-->
        <param name="contentDisposition">attachment;filename=${@java.net.URLEncoder@encode(filename, "UTF-8")}
        </param>
        <!--流名称-->
        <param name="inputName">inputStream</param>
    </result>
</action>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容