超全面 struts2 复习总结笔记

如有转载,请申明:
转载至 http://blog.csdn.net/qq_35064774/article/details/64934709

前言

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

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

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

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

目录

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

1.环境搭建

导包

  • 直接下载导入
    http://struts.apache.org/download.cgi
    可根据需要选择 all min src 等包,如果仅仅是学习,选择 min 包就够了

  • 版本管理工具导入

    • Gradle

      compile "org.apache.struts:struts2-core:2.5.10.1"
    • Maven


      org.apache.struts
      struts2-core
      2.5.10.1

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

    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 版本

```

    struts2
    org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter


    struts2
    /*

```

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

struts.xml

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



<struts>

struts>

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.jspresult>
    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();

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

是 的一个子元素,用于指定 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 中的常量。

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

通配符

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


<global-allowed-methods>regex:.*global-allowed-methods>

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

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

如上配置,我们访问 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"/>

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:

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 时使用,例如
    2、OGNL 中创建Map对象时使用,例如:
  • $
    1、在JSP中使用EL表达式时使用,例如 ${name}
    2、在xml配置文件中,编写OGNL表达式时使用,例如文件下载时,文件名编码。

    struts.xml——>${@java.net.URLEncoder.encode(filename)}
  • %
    在struts2中,有些标签的value属性取值就是一个OGNL表达式,例如
    还有一部分标签,value属性的取值就是普通字 符串,例如 ,如果想把一个普通的字符串强制看成时OGNL,就需要使用 %{} 把字符串套起来。
    例如 。当然在 也可以使用,但不会这么用。

投影

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

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 类似

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">falseparam>
            <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">18param>
            <param name="max">60param>
            <message>The age must be between ${min} and ${max}message>
        field-validator>
    field>
validators>

基于验证器的验证

<validators>
    <validator type="int">
        <param name="fieldName">ageparam>
        <param name="min">18param>
        <param name="max">60param>
        <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.1param>
            <param name="maxInclusive">50.1param>
            <message> The age must be between ${ minInclusive } and ${ maxInclusive }(含)message>
        field-validator>
    field>
    <field name="percentage2">
        <field-validator type="double">
            <param name="minExclusive">0.345param>
            <param name="maxExclusive">99.987param>
            <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-01param>
            <param name="max">2011-12-31param>
            <message>日期必须为2011年message>
        field-validator>
    field>
validators>

expression

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

<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>最大值必须大于最小值1message>
        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">param>
            <message> 用户名必须符合规范message>
        field-validator>
    field>
validators>

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 文件中配置自定义的拦截器










    /WEB-INF/page/hello.jsp



9.国际化

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

配置资源包

  • 全局资源包

<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:i18n name="com.xxxx.package">
    <s:text name="key"/>
s:i18n>

10.常见例子

防重复提交

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

    /success.jsp
  2. 使用 tokensession 或 token 拦截器
    表单中加入

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

    配置中加入 tokensession 拦截器

    name="login" class="xxxx">
        ref name="defaultStack" />
        ref name="tokensession" />
        <result type="redirect">/success.jspresult>
    

    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;
    }
}

修改上传文件大小限制


    <constant name="struts.multipart.maxSize" value="1048576"/>

限制上传文件扩展名

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

多文件上传需要把

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">
        
        <param name="contentType">application/octet-streamparam>
        
        <param name="contentDisposition">attachment;filename=${@java.net.URLEncoder@encode(filename, "UTF-8")}
        param>
        
        <param name="inputName">inputStreamparam>
    result>
action>

你可能感兴趣的:(JavaEE)