Struts2学习笔记(3)

1.   文件下载

1.1传统方式

/**

* TODO用传统的方式实现文件下载

 *优点:运用传统的web只是,好理解

 *缺点:在Action中引入了Servlet的api,导致struts2和servlet耦合

 */ publicclass DownloadActionextends ActionSupport {

      private StringfileName//要下载的文件的名字

      public StringgetFileName() {

          returnfileName;

      }

      publicvoid setFileName(StringfileName)throws Exception {

          /*

           *因为文件名是可能通过GET方式提交过来的,所以要转码

           * PSOT提交乱码问题框架自己会处理

           */

      if("GET".equalsIgnoreCase(ServletActionContext.getRequest().getMethod())){

                fileName=new String(fileName.getBytes("ISO-8859-1"),"UTF-8");

          }

          this.fileName = fileName;

      }

      public String download()throws Exception {

          //假设要下载的文件在/WEB-INF/download目录下

          HttpServletRequestrequest = ServletActionContext.getRequest();

          HttpServletResponseresponse = ServletActionContext.getResponse();

          //得到要下载的文件的输入流

          InputStreamis = ServletActionContext.getServletContext().getResourceAsStream("/WEB-INF/download/" +fileName);

          /*

           *判断用户使用的浏览器类型

           *分别处理文件下载时名字乱码问题

           */

          StringuserAgent = request.getHeader("User-Agent");//获取浏览器的类型 Firefox?IE?

          if(userAgent.contains("Firefox")) {//处理火狐下载文件名乱码问题,以UTF-8编码,用ISO-8859-1解码

                fileName =new String(fileName.getBytes("UTF-8"),"ISO-8859-1");

          }elseif(userAgent.contains("IE")) {//处理IE下载文件名乱码问题,以UTF-8编码

                fileName = URLEncoder.encode(fileName,"UTF-8");

          }

          response.setContentType("application/x-msdownload");//告诉客户端内容是下载类型的,要在获取输出流之前设置(经测试,没有这句也可以)

          response.setHeader("Content-Disposition","attachment;filename=" +fileName); //设置下载文件的在客户端显示的名字,中文会乱码(已处理)

          OutputStreamos = response.getOutputStream();

          byte[] buf =newbyte[1024];

          int len = 0;

          while((len =is.read(buf)) != -1) {

                os.write(buf,0, len);

                os.flush();

          }

          if(os !=null) os.close();

          if(is !=null) is.close();

          returnnull;//因为下载完成就可以关闭浏览器窗口了,无需显示页面,所以返回null

      }

}

1.2结合框架

1)请求的时参数有中文的话要先编码,虽然浏览器可会自动编码中文,但保险起见,自己代码编码。

       <%-- 使用jstl的<c:url>可以对get请求中的中文自动编码 --%>

       <c:url var="myurl"value="/download">

           <c:param name="fileName"value="图片.jpg"/>

       </c:url>

       <ahref="${myurl}">图片.jpg</a>

2)配置文件struts.xml中配置

          <actionname="download"class="com.maple.download.action.DownloadAction"method="execute">

                     <!--以二进制stream流的形式下载 -->

                <resultname="success" type="stream">

                     <!--下载内容的类型 -->

                     <paramname="contentType">image/jpg</param>

                     <!--以下载的形式输出给客户端

                          这里的${fileName}不是EL表达式,而是OGNL的语法

                           通过${属性名},可以在xml文件中访问Action的属性

                           中文下载时会乱码,要返回编码后的名字。

                      -->

                     <paramname="contentDisposition">attachment;filename=${fileName}</param>

                     <!--要下载的文件的输入流

                           底层调用的是Action中的getImageStream方法,该方法返回一个InputStream流

                        -->

                     <paramname="inputName">imageStream</param>

                     <!--以上参数的名字是固定的,可以参照org.apache.struts2.dispatcher.StreamResult的属性或文档 -->

                </result>

           </action>

3)Action中的代码

/**

 *DownAction.java

* TODO结合框架实现文件下载

 *注意:

 *Struts2没有提供文件下载拦截器,只用文件上传拦截器,

 *所以不是用拦截器实现文件下载的,而是以结果集(stream)

 *的形式下载的。

 *要在struts.xml中进行相关配置

 *该Action的作用主要的返回一个InputStream给框架

 */

publicclass DownloadActionextends ActionSupport {

      private StringfileName//要下载的文件的名字

public StringgetFileName()throws Exception {

          //返回用UTF-8编码后的名字,严格处理的话,这里还要判断浏览器的类型

          return URLEncoder.encode(fileName,"UTF-8");

      }

publicvoid setFileName(StringfileName)throws Exception {

          /*

           *因为文件名是可能通过GET方式提交过来的,所以要转码

           * PSOT提交乱码问题框架自己会处理

           */

      if("GET".equalsIgnoreCase(ServletActionContext.getRequest().getMethod())){

                fileName=new String(fileName.getBytes("ISO-8859-1"),"UTF-8");

          }

          this.fileName = fileName;

      }

public InputStreamgetImageStream()throws Exception {

          //假设要下载的文件在/WEB-INF/download目录下

          //得到要下载的文件的输入流

          InputStreamis = ServletActionContext.getServletContext().getResourceAsStream("/WEB-INF/download/" +fileName);

          return is;//要将得到的输入流返回框架

      }

}

2.基于XML文件的声明式验证

 1)手工验证执行顺序:validateXxx(争对Action中某个的业务方法验证)->validate(争对Action中所有的业务方法验证)

 2)XML声明式验证

   validate()------"Action的类名-validation"的xml文件------必须放置在与Action类同目录下。

validateXxxx()--"Action的类名-<action>标签中的name属性值-validation"的xml文件--必须放置在与Action类同目录。

 

验证指定Action的所有业务方法ValidatorAction-validation.xml:

<?xmlversion="1.0"encoding="UTF-8"?>

<!-- 该申明可以到xwork-core-xxx.jar包中的xwork-validator-1.0.2.dtd文件中拷贝 -->

 <!DOCTYPEvalidatorsPUBLIC

          "-//ApacheStruts//XWork Validator 1.0.2//EN"

          "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">

<!--  该文件在执行ValidatorAction中所有的业务方法时都会起作用,

  因为该文件名为ValidatorAction-validator.xml,且和ValidatorAction在同一目录下

  相当于代码验证时的validate()方法 -->

<validators>

 

      <!-- 要验证的字段,和表单的字段名一致 -->

      <field name="username">

          <!-- type指明使用框架内置的验证器

          如果一个字段有多个验证器,验证器的作用顺序是从上到下,和配置的

          顺序一致,只用前面的验证通过了后面的才能执行。

          在com.opensymphony.xwork2.validator.validators包中的default.xml中可以找内置的验证器

          requiredstring和required的区别:

          requiredstring要求必须是字符串

          required要求只要有值就可以

           -->

          <field-validatortype="requiredstring">

                <!--去除字段的空格,默认为true -->

                <paramname="trim">true</param>

                <!--验证不通过时的提示信息 -->

                <message>用户名不能为空</message>

            </field-validator>

      </field>

      <field name="password">

          <field-validatortype="requiredstring">

                <message>密码不能为空</message>

          </field-validator>

      </field>

      <field name="age">

          <field-validatortype="requiredstring">

                <message>年龄不能为空</message>

          </field-validator>

      </field>

      <field name="salary">

          <field-validatortype="requiredstring">

                <message>薪水不能为空</message>

          </field-validator>

      </field>

      <field name="birthday">

          <field-validatortype="requiredstring">

                <message>生日不能为空</message>

          </field-validator>

      </field>

</validators>

验证指定Action的某个业务方法ValidatorAction-xxx-validation.xml:

注意:xxx<action name=”xxx”>标签的name属性的值。和手工(代码)验证有点点区别。

<?xmlversion="1.0"encoding="UTF-8"?>

 <!DOCTYPEvalidatorsPUBLIC

          "-//ApacheStruts//XWork Validator 1.0.2//EN"

          "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">

<!--  该文件在执行ValidatorAction中的某个业务方法起作用,

  因为该文件名为ValidatorAction-xxx-validator.xml,且和ValidatorAction在同一目录下

  类似于代码验证时的validateXxx()方法 -->

<validators>

 

      <field name="username">

          <!--使用框架内置的正则表达式验证器

          处理类com.opensymphony.xwork2.validator.validators.RegexFieldValidator

           -->

          <field-validatortype="regex">

                <paramname="expression">[\u4E00-\uFA29]+</param>

                <message>用户名必须为中文</message>

          </field-validator>

      </field>

      <field name="password">

          <field-validatortype="regex">

                <paramname="expression">[0-9]{6}</param>

                <message>密码必须为6位数字</message>

          </field-validator>

      </field>

      <field name="age">

          <field-validatortype="int">

                <paramname="min">1</param>

                <paramname="max">120</param>

                <message>年龄在1到120之间</message>

          </field-validator>

      </field>

      <field name="salary">

          <field-validatortype="double">

                <paramname="minInclusive">4000</param>

                <paramname="maxInclusive">8000</param>

                <message>薪水必须在4000到8000之间,包含4000和8000</message>

          </field-validator>

      </field>

      <field name="birthday">

          <field-validatortype="date">

                <paramname="min">1970-01-01</param>

                <paramname="max">2050-01-01</param>

                <message>生日必须在1970-01-01到2050-01-01之间</message>

          </field-validator>

      </field>

</validators>

当存在二种验证文件时,其结果是二者验证效果之和,先执行ValidatorAction-validation.xml文件->后执行ValidatorAction-xxx-validation.xml文件。

对比:当ValidatorAction中,有validate(),又有validateXxx()的话,会先执行validateXxx()

再执行validate(),二者验证是[叠加]的效果。

3.OGNL(对象图导航语言)

3.1概念

OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,

它是一个开源项目。Struts2框架使用OGNL作为默认的表达式语言,而不是EL。

OGNL表达式可以完成:

   1、访问OGNL上下文(OGNL context)和ActionContext;

   2、操作集合对象;

3、Ognl通常和struts2的标签结合使用,不能独立使用。

3.2 Struts2的数据中心(ActionContext对象)

1)ActionContxt对象是整个Struts2的数据中心,包含六个对象,requestMap,sessionMap,applicationMap,parameters,attr,ValueStack(值栈)

注意:pageContext对象不属于数据中心的对象。

3.3某个Action的数据中心(ValueStack对象,即值栈)

ValueStack(值栈)实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础。

 

ValueStack(值栈): 贯穿整个 Action 的生命周期(每个 Action类的对象实例都拥有一个

ValueStack 对象). 相当于一个数据中心. 在其中保存当前Action对象和其他相关对象.

Struts 框架把 ValueStack 对象保存request中。

Action的对象一旦产生,值栈就产生了,位于request中,Action的对象一旦销毁,值栈也就销毁了,每一次请求都会产生值栈。

 

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

•        ObjectStack: Struts  把动作和相关对象压入 ObjectStack 中—List。

•        ContextMap: Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中。

也可以按以下说法理解:

值栈分为二部分(List区域和Map区域)

List:  存放Action产生的实例和Action中所有的属性。

 

Map:在Action中能够取得的一切map对象都放置Map区域。

 

Struts2接受一个请求时,会迅速创建ActionContextValueStackaction。然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。

注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:propertyvalue="name"/>通过ongl获取值的时候,如果没有获取,则返回空字符串,即“”。

3.4 ongl中#的用法

#相当于ActionContext.getContext(),使用#xxx来获取值的时候,会直接去值栈中找,如果要找的值不在值栈中则返回空字符串。此时需要指定查找的范围,才能找到指定的值。

可以存放数据的返回有:request,session,application,parameters,ValueStack五个(即数据中心除attr对象外其他对象都可以存放数据)

如:

#request.username(也可以写为:#request[“username”])  到requestMap中找key为username对应的值。相当于request.getParameter(“username”).

#username 到值栈中找key为username对应的值,即不写任何范围时,默认到值栈中找。

#parameters.username获取参数中名为username的值,多个同名的话返回数组,指定下标(从0开始)可以获取某个参数值。如#parameters.username[1]获取第二参数值。

特别注意:

attr对象不能存储数据,它主要用于从其他五个范围里和pageContext中找指定key的值。

如:

#attr.username 获取keyusername的对应的值,获取的顺序为:

pageContext对象(不是)-requestMap对象-valuestack对象-sessionMap对象-applicationMap对象找到就返回,不再往下找了,找到最后没找到就返回空字符串""

 

 

1)作用于struts2的域对象,而不是普通域对象,不过底层还是操作普通的域对象,struts2对普通的域对象进行了封装。

      <%

          pageContext.setAttribute("username","pageContext_username");

      %>

      <!-- 通过ongl中的#可以到指定的域中获取值,获取值栈中的值不用写# -->

      值栈ValueStack中的值:<s:propertyvalue="username"/><br/>

      requestMap中的值:<s:propertyvalue="#request.username"/><br/>

      sessionMap中的值:<s:propertyvalue="#session.username"/><br/>

      applicationMap中值:<s:propertyvalue="#application.username"/><br/>

      parameters中值:<s:propertyvalue="#parameters.username"/><br/><!--获取参数名为username的值,多个的话返回数组 -->

      通过attr对象获取值:<s:propertyvalue="#attr.username"/><!--这里获取到的值是:pageContext_username -->

      <!-- attr是struts2数据中心的对象之一,专门用来从struts2的域对象中取值

      取值的顺序为:

      pageContext对象-requestMap对象-valuestack对象-sessionMap对象-applicationMap对象

      找到就返回,不再往下找了,找到最后没找到就返回空字符串""

      注意:pageContext对象不是struts2的数据中心之一。 -->

 

2)作用域JavaBean对象,取出JavaBean对象的属性值。

      <%

          List<User>users =new ArrayList<User>();

          users.add(new User(1,"张三", 20));

          users.add(new User(2,"李四", 22));

          users.add(new User(3,"王五", 23));

          users.add(new User(4,"赵六", 25));

          //将users放到pageContext中

          pageContext.setAttribute("users", users);

      %>

      <table border="1">

          <tr>

                <th>编号</th>

                <th>姓名</th>

                <th>年龄</th>

          </tr>

      <%-- 通过<s:iterator>标签遍历取出user --%>

          <s:iteratorvalue="#attr.users"var="user">

                <tr>

                     <td><s:propertyvalue="#user.id"/></td>

                     <td><s:propertyvalue="#user.name"/></td>

                     <td><s:propertyvalue="#user.age"/></td>

                </tr>

          </s:iterator>

      </table>

 

 

 

3)作用于普通字符串

 

如果value只是一个字符串的话,不是JavaBean对象,此时加#号,还是不加#,都可以。

<s:property value="#username"/> 等价于<s:property value="username"/>

        

<%-- 通过<s:iterator>标签遍历取出user的指定的属性值,即集合的投影(过滤) --%>

          <s:iteratorvalue="#attr.users.{name}"var="name">

                <tr>

                     <td></td>

                     <!--当要获取的key对应的值为字符串时,#号可有可无-->

                     <td><s:propertyvalue="name"/></td>

                     <td></td>

                </tr>

           </s:iterator>

4)集合的投影(过滤)

 

集合的过滤有以下三种方式:
     a.“?#”:过滤所有符合条件的集合,如:users.{?# this.age > 19};
     b.“^#”:过滤第一个符合条件的元素,如:users.{^# this.age > 19};
     c.“$#”:过滤最后一个符合条件的元素,如:users.{$# this.age > 19} 。
this表示集合中的某个元素。

投影(过滤)操作返回的是一个集合,可以使用索引取得集合中指定的

元素,如:users.{?#this.age > 19}[0]。

A. 取出所有年龄大于22岁的所有用户:<s:iterator value="#attr.users.{?#this.age> 22}" var="user" >

B.  取出所有年龄大于22岁的第一个用户:<s:iterator value="#attr.users.{^#this.age> 22}" var="user" >

C.  取出所有年龄大于22岁的最后一个用户:<s:iterator value="#attr.users.{$#this.age> 22}" var="user" >

D. 取出所有年龄大于22岁的第二个[索引从0开始]用户:<s:iteratorvalue="#attr.users.{?#this.age >22}[1]" var="user" >

 

5)用#构造Map集合

 

该功能常用在给radio或select、checkbox等标签赋值上。

      <%-- 通过#{key:value, key:value}的形式构造,类似json格式 --%>

      <s:iterator value="#{'male': '男', 'female' : '女'}">

          <!--通过指定其key[固定]value[固定]取出其对应的键和值 -->

          <s:propertyvalue="key"/>=<s:propertyvalue="value"/><br/>

      </s:iterator>

      <%-- 通过{value1, value2}的形式构造简写 --%>

      <s:iterator value="{'男', '女'}">

          <s:property/>

      </s:iterator>

 

6)用#构造List集合

创建list与创建map语法很相似,不同的是list前不需要加"#"号.

<s:iterator value="{1,2,3,4}">

    <s:property/>   <br>

 </s:iterator>  

 <s:iterator value="{'s1','s2','s3','s4'}"var="s">

    <s:property value="#s"/> <br>

 </s:iterator>

7)%的用法

 

“%”符号的用途是在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式。

      <!-- 不使用%{}时,是ongl表达式,通常都这样做 -->

      <s:property value="#request.username"/><br/>

      <!-- %{}里面的内容是ongl表达式 -->

      <s:property value="%{#request.username}"/><br/>

      <!-- %{}里面的内容用引号引起来时,不再是ongl表达式,而是普通的字符串,原样输出 -->

      <s:propertyvalue="%{'#request.username'}"/><br/>

 

8)$的用法

 

在struts2配置文件中引用ognl表达式 ,引用的是值栈的值 。

A.读取xml文件中配置的变量

      <field-validatortype="double">

                <paramname="minInclusive">4000</param>

                <paramname="maxInclusive">8000</param>

                <message>薪水必须在${minInclusive}${maxInclusive}之间,包含${minInclusive}${maxInclusive}</message>

           </field-validator>

B.读取Action类中的实例变量,底层执行getXxxx()方法

<!--

这里的${fileName}不是EL表达式,而是OGNL的表达式,通过${属性名},可以在xml文件中访问Action的属性-->

<param  name="contentDisposition">attachment;filename=${fileName}</param>

4.Struts2防止表单重复提交

 A.框架提供了token拦截器,主要用于防止表单重复提交。

B.默认栈中无token拦截器,所以在<action>标签中要显示引用token拦截器,此时也要显式引用默认拦截器。

 C.开发步骤:

1)在jsp页面中使用<s:token/>,自动产生hidden框,同时在服务端的session中放置一份id值。

<s:formaction="token"method="post"namespace="/">

           <!--在表单总添加该标签后,自动产生hidden框,同时在服务端的session中放置一份id值。 -->

           <s:token/>

           <s:textfieldlabel="用户名"name="username"/>

           <s:passwordlabel="密码"name="password"showPassword="true"/>

           <s:submitvalue="注册"/>

           <s:resetvalue="清空"/>

     </s:form>

2)在<action/>标签中,显示引用token拦截器。

<packagename="token"namespace="/"extends="struts-default">

           <actionname="token"class="com.maple.token.action.TokenAction"method="execute">

                 <resultname="success"type="dispatcher">/WEB-INF/token_success.jsp</result>

<!-- 重复提交时返回的结果是invalid.token[固定],可以在源码中查得 -->

<resultname="invalid.token"type="dispatcher">/WEB-INF/token_repeat.jsp</result>

<!-- 显式引用token拦截器,因为在struts.xml文件中只是注册的token拦截器并没有将其放到默认拦截器栈中 -->

<interceptor-refname="token"/>

<!-- 这里必须显式引用默认拦截器栈,因为一旦显式引用了其他拦截器就没有默认拦截器了 -->

                 <interceptor-ref name="defaultStack"/>

           </action>

     </package>

 

3)配置一个重复提交要转到的页面。

      <!--重复提交时返回的结果是invalid.token[固定],可以在源码中查得 -->

<resultname="invalid.token"type="dispatcher">/WEB-INF/token_repeat.jsp</result>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值