编程(Java-Struts)
Nov 18
SiteMesh 入门 结合网上和官网的,如果英文ok 的话,建议看官网比较全。
下载:http://wenku.baidu.com/view/99a362eb172ded630b1cb660.html

SiteMesh 入门

一个优于Apache TilesWeb页面布局、装饰框架
一、SiteMesh项目简介
OS(OpenSymphony)SiteMesh是一个用来在JSP中实现页面布局和装饰(layout and decoration
的框架组件,能够帮助网站开发人员较容易实现页面中动态内容和静态装饰外观的分离。
Sitemesh是由一个基于Web页面布局、装饰以及与现存Web应用整合的框架。它能帮助我们在由大
量页面构成的项目中创建一致的页面布局和外观,如一致的导航条,一致的banner,一致的版权,等等。
它不仅仅能处理动态的内容,如jspphpasp等产生的内容,它也能处理静态的内容,如htm的内容,
使得它的内容也符合你的页面结构的要求。甚至于它能将HTML文件象include那样将该文件作为一个面板
的形式嵌入到别的文件中去。所有的这些,都是GOFDecorator模式的最生动的实现。尽管它是由java语言来实现的,但它能与其他Web应用很好地集成。
官方:http://www.opensymphony.com/sitemesh/
下载地址:http://www.opensymphony.com/sitemesh/download.action 目前的最新版本是Version 2.4.1
二、为什么要使用SiteMesh?
我们的团队开发J2EE应用的时候,经常会碰到一个比较头疼的问题:
由于Web页面是由不同的人所开发,所以开发出来的界面通常是千奇百怪,通常让项目管理人员苦笑不得。
而实际上,任何一个项目都会要求界面的统一风格和美观,既然风格统一,那就说明UI层肯定有很多可以抽出来
共用的静态或动态部分;如何整合这些通用的静态或动态UI呢?Apache Tiles框架站了出来很好的解决了这一问题,
再加上他与struts的完美集成,导致大小项目都把他作为UI层的首选框架,
但是:
Tiles确实有着它很多的不足之处,下文我会介绍,本文想说的是,除了Apache Tiles框架,其实我们还有更好的解决方案,那就是:SiteMesh
本文
介绍了一个基于Web页面的布局、装饰以及应用整合的框架Sitemesh,它能帮助你为你的应用创建一致的外观,
很好的取代Apache Tiles;
三、SiteMesh VS Apache Tiles
用过struts的朋友应该对Apache Tiles的不会陌生,我曾经有一篇文章介绍过strutstiles框架的组合与继承,
现在怎么看怎么觉得复杂;
从使用角度来看,Tiles似乎是Sitemesh标签<page:applyDecorator>的一个翻版。其实sitemesh最强的一个特性是sitemeshdecorator模式用在过滤器上。任何需要被装饰的页面都不知道它要被谁装饰,所以它就可以用来装璜来自phpaspCGI等产生的页面了。你可以定义若干个装饰器,根据参数动态地选择装饰器,产生动态的外观以满足你的需求。它也有一套功能强大的属性体系,它能帮助你构建功能强大而灵活的装饰器。
相比较而言,在这方面Tiles就逊色许多。
个人觉得在团队开发里面,Apache Tiles框架会导致所有人不仅仅要了解并且清楚Apache Tiles的存在,
并且要特别熟悉每一个Tiles layout模板的作用,否则就可能出现用错模板的情况;除此之外,每个人涉及到
的所有WEB页面都需要去配置文件里面逐个配置,不仅麻烦出错的几率还高;
以上所有的不足都是SiteMesh所不存在的;
四、SiteMesh的基本原理
一个请求到服务器后,如果该请求需要sitemesh装饰,服务器先解释被请求的资源,然后根据配置文件获得用于该请求的装饰器,最后用装饰器装饰被请求资源,将结果一同返回给客户端浏览器。
SiteMesh设计思想是,用户发送request至服务器,服务器根据此request生成动态数据,生成网页,准备返回给客户端。就在返回前,SiteMesh进行拦截,对此网页进行解析,将titlebody等部分拆解出来,套上模板后,再返回给客户端。由于SiteMesh在返回客户端的最后一步工作,此时的网页已经具备了标准的html网页格式,因此SiteMesh只需解析标准的html网页,无需考虑各个Web应用是应用了JSPASP,还是Velocity技术,相当灵活。
SiteMesh使用了Decorator的设计模式
五、如何使用SiteMesh
1 添加基础包和文件:
a sitemesh-2.4.1.jar
b sitemesh-decorator.tld
c sitemesh-page.tld
后面两个是xml 定义文件,tomcat 版本高的可以不引用,否则需要在web.xml下配置(详看readme)
<jsp-config>
<taglib>
<taglib-uri>http://www.opensymphony.com/sitemesh/decorator</taglib-uri>
<taglib-location>/WEB-INF/lib/sitemesh-decorator.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://www.opensymphony.com/sitemesh/page</taglib-uri>
<taglib-location>/WEB-INF/lib/sitemesh-page.tld</taglib-location>
</taglib>
</jsp-config>
最基本的可以参考:sitemesh-blank.war
2 web.xml 配置:
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这里定义了一个过滤器.所有的请求都交由sitemesh来处理
WEB-INF下创建一个decorators.xml文件,内容如下:
<decorators defaultdir="/decorators">
<decorator name="main" page="/decorator/decorator.jsp">
<pattern>/*</pattern>
</decorator>
<!-- excludes标签中定义不会被装饰的JSP视图界面 -->
<excludes>
<pattern>/jsp/layout/*</pattern>
<pattern>/jsp/template/*</pattern>
<pattern>/jsp/image.jsp</pattern>
</excludes>
<!-- 定义装饰器,该装饰器为一个页面格式分为上中下三块的JSP视图界面-->
<decorator name="template" page="3PartLayoutTemplate.jsp">
<pattern>/jsp/*</pattern>
<pattern>/jsp/body/*</pattern>
</decorator>
</decorators>
这是定义了模板页,也就是所有页面在返回给客户端之前,先在这里加上装饰,套上模板。
defaultdir="/decorators"说明了模板页的路径.(默认是可以不配置,'/' 即根目录)<decorator name="main" page="main.jsp">模板页的名称。
<pattern>/*</pattern>表示对所有的response进行处理
defaultdir: 包含装饰器页面的目录
page : 页面文件名
name : 别名
role : 角色,用于安全
webapp : 可以另外指定此文件存放目录
Patterns : 匹配的路径,可以用*,那些被访问的页面需要被装饰。
下面开始一个最简单的配置:
1 加入包 sitemesh-2.4.1.jar
2 web.xml 新增过滤器:
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3 新建decorators.xml 配置装饰器
<decorators defaultdir="/decorators">
<!-- 定义装饰器,该装饰器为一个页面格式分为上中下三块的JSP视图界面-->
<decorator name="main" page="/decorator/decorator.jsp">
<pattern>/*</pattern>
</decorator>
<!-- excludes标签中定义不会被装饰的JSP视图界面 -->
<excludes>
</excludes>
</decorators>
4 装饰器页面 /decorator/decorator.jsp 注意路径以根路径webroot开始,编码utf-8
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="java.util.*" %>
<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My Site -<decorator:title default="Welcome! 装饰器页面。。。" /></title>
<decorator:head></decorator:head>
</head>
<body>
<p><font color="red">before body</font> </p>
<decorator:body ></decorator:body>
<p><font color="red">After body</font></p>
</body>
</html>
5 新建被装饰的页面index2.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Agent Test </title>
</head>
<body>
我是访问index2.jsp 单独的一条语句:
</body>
</html>
6 部署访问http://192.168.1.74:8085/struts2.2lxDemo/index2.jsp

简单Demo 结束;~ ~
其他:
可以结合原有的velocity 或者freemarker 页面框架,在原来的定义装饰器的文件decorators.xml 添加:

<decorator name="velocity" page="velocity.vm">

<pattern>/velocity.html</pattern>
</decorator>
<decorator name="freemarker" page="freemarker.ftl">
<pattern>/freemarker.html</pattern>
</decorator>
在与strut结合时需要引入:
1 struts2-sitemesh-plugin-2.0.8.jar
2 注意过滤器的位置:应该在struts2org.apache.struts2.dispatcher.FilterDispatcher过滤器之前org.apache.struts2.dispatcher.ActionContextCleanUp过滤器之后,否则会有问题;
3 sturts访问的时候记得重定向:其他不变

<!----------------------文件名:struts.xml---------------->

…………
<action name="index" >
<result type="redirect">/jsp/body/firstPage.jsp</result>
</action>
…………
4 如果decorator.xml 需要定义到其他位置,可以通过。xml配置:
在下载的SiteMesh包中找到sitemesh.xml(\sitemesh-2.3\src\example-webapp\WEB-INF目录下就有)将其拷贝到/WEB-INF目录下;
sitemesh.xml文件中有一个property结点(如下),该结点指定了decorators.xml在工程中的位置,让sitemesh.xml能找到他;
按照此路径新建decorators.xml文件,当然这个路径你可以任意改变,只要property结点的value值与其匹配就行;

<property name="decorators-file" value="/WEB-INF/sitemesh/decorators.xml"/>

六、实战感受
刚刚做完一个用到sitemesh的项目,跟以前用tiles框架相比,最大的感受就是简单,系统设计阶段
就把模板文件和sitemesh框架搭好了!哪些页面使用框架哪些不使用,全部都通过UI Demo很快就定义出来了;
在接下来的开发中所有成员几乎感受不到sitemesh的存在,各自仅仅关心自己的模块功能实现;
更多可以访问:

http://www.javaeye.com/topic/509015 主题:Struts2讲义44 Struts2 页面布局实现

http://www.javaeye.com/topic/509710 主题:Struts2讲义45 10.2 sitemesh 高级应用
http://www.javaeye.com/topic/510309
主题:Struts2讲义46 <decorator:usePage> <decorator:useHtmlPage> <decorator:head> 标签的使用介绍
http://www.blogjava.net/yjhmily/archive/2007/09/28/149143.htm
一个优于Apache TilesWeb页面布局、装饰框架
http://www.javaeye.com/topic/190031 主题:SiteMesh入门
http://hkme.javaeye.com/blog/518656 SiteMesh教程
http://www.docin.com/p-46157733.html#
http://blog.csdn.net/kongxx/archive/2007/05/13/1606901.aspx
之前也已经说过原来的 sitemesh 装饰标签值能显示 <head> <body> 之间的内容,不能显示它们的属性。但是使用 <decorator:usePage> <decorator:useHtmlPage> 就能显示这些属性的内容。
七、总结
使用sitemesh给我们带来的是不仅仅是页面结构问题,它的出现让我们有更多的时间去关注底层业务
逻辑,而不是整个页面的风格和结构。它让我们摆脱了大量用include方式复用页面尴尬局面,也避免了tiles
框架在团队开发中的复杂度,它还提供了很大的灵活性以及给我们提供了整合异构Web系统页面的一种方案。

代码解释
1 ) 首先说明开发中如果要使用 sitemesh 来进行页面布局功能开发,在 WEB-INF/lib 文件夹下一定要放入 sitemesh 支持的 jar 包。目前 sitemesh 最新版本是 sitemesh-2.3.jar
2 )然后在 web.xml 中必须定义 sitemesh 为过滤器。这样 Web 项目中的 JSP 或其他视图界面都可以被 sitemesh 调用进行页面布局。然后新建 decorators.xml 文件。该文件是用来定义所有装饰器视图界面。因为 sitemesh 是使用设计模式中的装饰模式来进行页面布局功能开发(具体装饰模式定义读者可查看相关设计模式资料)。在这里笔者只能简单说明 sitemesh 将视图页面分为装饰页面和被装饰页面两种。装饰页面又称之为装饰器。都是在 decorators.xml 文件定义,一般定义装饰器时候都指定了被装饰页面或者某文件路径,在该文件路径下的所有视图界面文件都是被装饰页面。
读者可以查看 decorators.xml 文件代码看到 <excludes> 标签,在该标签中一般都是指定不需要被装饰的视图文件。以 <pattern> 标签来一个个定义。有代码也可知道“ /jsp/layout/* ”标明在 /jsp/layout/ 目录下所有的视图界面都是不需要被装饰的。如果想指定某个视图文件不被装饰,则可以写成代码中/jsp/image.jsp形式,这样在 jsp 目录下只有 image.jsp 文件不被装饰,其他视图界面都是要被装饰的。
<decorator> 标签则是相关装饰器页面定义需要使用的标签。其中 name 属性是让开发者定义装饰器名字,而 page 属性则指定装饰器文件的路径和具体名字。在本示例代码中可知 3PartLayoutTemplate.jsp 是一个装饰器文件。
注意:在<decorators> 标签中有个defaultdir 属性,它是表明装饰器文件的路径。本示例代码中定义的是“/jsp/template ”, 也就是说之前所定义的3PartLayoutTemplate.jsp 是在该目录下。如果defaultdir 定义的是“/ ”,则装饰器定义的page 属性中定义的装饰器文件是在Web 项目根目录下。在开发环境中就是在WebRoot 文件夹下(笔者使用的是MyEclipse ,所以读者使用的开发工具不是MyEclipse 的话,则web 根目录的文件夹名字不一定是WebRoot )。如果读者想让3PartLayoutTemplate.jsp 还是在/jsp/template 目录下,而defaultdir 想定义为“/ ”,则page 属性中定义需要写成“/jsp/template/3PartLayoutTemplate.jsp ”
在装饰器定义中,可以还是使用<pattern> 标签来指定需要被装饰的视图界面。具体做法和<excludes> 中的<pattern> 用法相同,只不过一个是指定被装饰的,另外一个指定不被装饰的。当然也可以不使用<pattern> 标签,这样可以使用 <page:applyDecorator> 标签在页面中指定需要被装饰的视图界面文件(下一小节将具体介绍如何使用),而不是在 decorators.xml 文件中指定被装饰的视图界面文件。
3 )在 3PartLayoutTemplate.jsp 该装饰器页面中,读者查看代码可以看见在该视图文件中,笔者先定义了 “<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"%> ”用以在页面中使用<decorator> 标签 。这里只是使用了 <decorator:title> < decorator:body> 两个 decorator 的基础标签。
这里 <decorator:title> 中有个 default 属性。使用该属性的目的是当被装饰视图界面文件中没有定义自己的 <title> 标签内容时候,则装饰和被装饰视图界面一起显示的页面布局界面的 title 就显示该属性中内容。在图 10.1 中,笔者也用红框特别注明了显示的 title 的内容。
< decorator:body> 就是表明被装饰视图界面的 <body> 中内容显示。因此可以这么理解在装饰器视图界面文件中,如果看到以 decorator 打头的标签,则是显示被装饰视图界面文件的内容。如果后写的是 title 则显示的是被装饰文件中 <title> 标签中的内容。如果是 body 则显示的是被装饰文件中 <body> 标签中的内容。
在本示例中笔者将 /jsp/body/ 中的 firstPage.jsp 作为被装饰文件,在 web.xml 中定义缺省显示的就是该被装饰文件。因此运行该示例就可以看到 firstPage.jsp 不仅仅显示了自己的内容,而且将装饰器文件 3PartLayoutTemplate.jsp 中的内容也显示出来了,特别是两个<jsp:include> JSP 文件。给人感觉就是在3PartLayoutTemplate.jsp 显示的内容上面又加了 firstPage.jsp 内容。而被装饰的文件还可以是其他视图文件。因此在别的界面中则显示的不一定是 firstPage.jsp 内容,但是 3PartLayoutTemplate.jsp 中的内容还是会先出来的。这样就达到了页面布局的效果。
注意:开发人员还可以定义Action 来显示页面布局。在笔者使用的该示例的struts.xml 中定义了index.action ,而其中result 指向的就是 firstPage.jsp 。不过这时候 result type 类型一定要是redirect(代码中黑体注明,另外说一句 result 缺省的类型是 dispatcher ,可以不显示声明)。因为如果不定义该类型,则 index.action 指向的只是单纯 firstPage.jsp 文件的内容,不会显示装饰器文件 3PartLayoutTemplate.jsp 中的内容。这和redirect 是一个重定向有关系,有关重定向的概念笔者在第二章也做过简单介绍,读者可以翻阅之前章节。

Tags:
Oct 11

from:http://hi.baidu.com/woaini5730/blog/item/46b3da34a408493c5ab5f551.html

 

**************************************Service层方法****************************

public PageModel getSearch(String key, int articletypeid,int pageNo, int pageSize) //分页--搜索
{
   PageModel pagemodel = artircleDao.getSearch (key , articletypeid , pageNo , pageSize);
   List keys = SubString.getInstance().splitKey(key);
   if(pagemodel.getList()!=null)
   {
    ListArticle.getInstance().color(pagemodel.getList() , keys);
    ListArticle.getInstance().listDeal(pagemodel.getList() , 1 , 50);
   }
   return pagemodel;
  
}

*******************************************Dao层方法**************************************

public PageModel getSearch (String key ,final int articletypeid,final int pageNo, final int pageSize) //分页--搜索
{
   final List keys=SubString.getInstance().splitKey (key);   //方法将在后面给出
   
   PageModel pageModel = new PageModel();
   List itemList = new ArrayList();
    if(keys.size()>0)
    {
    
         itemList=(List)this.getHibernateTemplate().execute(
           new HibernateCallback()
        {  
         public Object doInHibernate(org.hibernate.Session session)
         {    
                String hql="from Article a where a.title like ? "; //对标题的搜索
              for(int i=0;i<keys.size()-1;i++)//如果只有一个关键字 则不加
              {
             hql=hql+"or a.title like ?";
              }
             
              hql= hql+"or a.content like ?"; //添加内容的搜索
              for(int i=0;i<keys.size()-1;i++)//如果只有一个关键字 则不加
              {
             hql=hql+"or a.content like ?";
              }
              Query q = session.createQuery(hql+" order by a.id");
             
             
              for(int i= 0 ;i<keys.size();i++) //添加对标题的参数 从i开始
              {
             q.setParameter(i, "%"+keys.get(i) + "%");
              }
              for(int i= 0 ;i<keys.size();i++) //添加对内容的参数 从keys.size()+i开始
              {
             q.setParameter(keys.size()+i, "%"+keys.get(i) + "%");
              }
            q.setFirstResult((pageNo - 1) * pageSize)
            .setMaxResults(pageSize);
            
                            List result = q.list();
             return result;
         }
        }
   
         );
     
        
         List itemList_2=(List)this.getHibernateTemplate().execute( //用于总记录数
           new HibernateCallback()
        {  
         public Object doInHibernate(org.hibernate.Session session)
         {    
                String hql="from Article a where a.title like ? "; //对标题的搜索
              for(int i=0;i<keys.size()-1;i++)//如果只有一个关键字 则不加
              {
             hql=hql+"or a.title like ?";
              }
             
              hql= hql+"or a.content like ?"; //添加内容的搜索
              for(int i=0;i<keys.size()-1;i++)//如果只有一个关键字 则不加
              {
             hql=hql+"or a.content like ?";
              }
              Query q = session.createQuery(hql+" order by a.id");
             
             
              for(int i= 0 ;i<keys.size();i++) //添加对标题的参数 从i开始
              {
             q.setParameter(i, "%"+keys.get(i) + "%");
              }
              for(int i= 0 ;i<keys.size();i++) //添加对内容的参数 从keys.size()+i开始
              {
             q.setParameter(keys.size()+i, "%"+keys.get(i) + "%");
              }
           
           
            
                            List result = q.list();
             return result;
         }
        }
   
         );
           pageModel.setList(itemList);
        pageModel.setPageNo(pageNo);
        pageModel.setPageSize(pageSize);
        pageModel.setTotalRecords(itemList_2.size());//-----------
   
   }
   return pageModel;
  
}

******************************* splitKey() *************************************************

public List splitKey (String key)//格式化关键字 去除多余的空格
   {  
    key = key.trim();
    String[] keys = key.split(" ");
    List keyList= new ArrayList();
    for(String k : keys)
    {
     if(k.equals("")||k.equals(" ")||k==null)
      continue;
     else
     {
      //System.out.println("kkkkkkkkkkkkkkk==:"+k);
      keyList.add(k);
     }
     
    }
    return keyList;
   }

*********************************PageModel类 *****************************************

package com.bcm.util;

import java.util.List;

import com.bcm.model.ArticleType;

public class PageModel
{
// 总记录数
private int totalRecords;

// 结果集
private List list;

// 当前页
private int pageNo;

// 每页显示多少条
private int pageSize;

private int getTotalPages; //总页数


private ArticleType articletype;

public int getTotalRecords()
{
   return totalRecords;
}

public void setTotalRecords(int totalRecords)
{
   this.totalRecords = totalRecords;
}

public List getList()
{
   return list;
}

public void setList(List list)
{
   this.list = list;
}

public int getPageNo()
{
   return pageNo;
}

public void setPageNo(int pageNo)
{
   if(pageNo==0)
       this.pageNo = 1;
   else
    this.pageNo = pageNo;
}

public int getPageSize()
{
   return pageSize;
}

public void setPageSize(int pageSize)
{
   this.pageSize = pageSize;
}

/**
* 取得总页数
* @return
*/
public int getTotalPages()
{
   return (totalRecords + pageSize - 1) / pageSize;
}

/**
* 取得第一页
* @return
*/
public int getTopPageNo()
{
   return 1;
}

/**
* 取得上一页
* @return
*/
public int getPreviousPageNo()
{
   if (pageNo <= 1)
   {
    return 1;
   }
   return pageNo - 1;
}

/**
* 取得下一页
* @return
*/
public int getNextPageNo()
{
   if (pageNo >= getTotalPages())
   {
    return getTotalPages() == 0 ? 1 : getTotalPages();
   }
   return pageNo + 1;
}

/**
* 取得最后一页
* @return
*/
public int getBottomPageNo()
{
   return getTotalPages() == 0 ? 1 : getTotalPages();
}

public ArticleType getArticletype()
{
   return articletype;
}

public void setArticletype(ArticleType articletype)
{
   this.articletype = articletype;
}

 

}

********************************前台调用分页************************************************

<table width="926" height="21" border="0" class="title1">
              <tr>
        <form action="article_search.action" ><!-- 只用于手动跳转到XX页 -->
     <td height="25" align="center" valign="bottom" class="titlep">
     
      <s:url id="url_top" value="article_search.action">
             <s:param name="pageNo" value="1"></s:param>
             <s:param name="key" value="#session.key"></s:param>
       </s:url>
     
       <s:url id="url_pre" value="article_search.action">
             <s:param name="pageNo" value="pagemodel.getPreviousPageNo()"></s:param>
             <s:param name="key" value="#session.key"></s:param>
       </s:url>
      
       <s:url id="url_next" value="article_search.action">
             <s:param name="pageNo" value="pagemodel.getNextPageNo()"></s:param>
             <s:param name="key" value="#session.key"></s:param>
       </s:url>
      
       <s:url id="url_buttom" value="article_search.action">
             <s:param name="pageNo" value="pagemodel.getBottomPageNo()"></s:param>
             <s:param name="key" value="#session.key"></s:param>
       </s:url>
       共<font color="#CC3333"><s:property value="pagemodel.totalRecords"/></font>条
        每页<font color="#CC3333"><s:property value="pagemodel.pageSize"/></font>条
         第<font color="#CC3333"><s:property value="pagemodel.pageNo"/>/<s:property value="pagemodel.getTotalPages()"/></font>页
        
          <s:if test="pagemodel.pageNo==1&&pagemodel.getTotalPages()==1">
                  <font color="#999999"> 首页</font>
                 <font color="#999999"> 上一页</font>
                 <font color="#999999">下一页</font>
                 <font color="#999999">尾页</font>
          </s:if>
          <s:elseif test="pagemodel.pageNo==1&&pagemodel.getTotalPages()!=1">
             <font color="#999999"> 首页</font>
             <font color="#999999"> 上一页</font>
             <s:a href="%{url_next}">下一页</s:a>
             <s:a href="%{url_buttom}">尾页</s:a>
         </s:elseif>
          <s:elseif test="pagemodel.pageNo==pagemodel.getBottomPageNo()">
                <s:a href="%{url_top}">首页</s:a>
                <s:a href="%{url_pre}">上一页</s:a>
                <font color="#999999">下一页</font>
                <font color="#999999">尾页</font>
                                             
          </s:elseif>
         <s:else>
             <s:a href="%{url_top}">首页</s:a>
             <s:a href="%{url_pre}">上一页</s:a>
             <s:a href="%{url_next}">下一页</s:a>
             <s:a href="%{url_buttom}">尾页</s:a>
         </s:else>
       
         转到<input type="hidden" name="key" value="<s:property value="#session.key"/>"/>
      <input type="text" name="pageNo" size="2"/>页
       <input type="submit" value="go"/>
       
     </td>
</form>
              </tr>
</table>  
from:http://hi.baidu.com/woaini5730/blog/item/46b3da34a408493c5ab5f551.html

Sep 8

1. 文件上传的原理:
表单元素的enctype属性指定的是表单数据的编码方式,该属性有3个值:
1) application/x-www-form-urlencoded:这是默认编码方式,它只处理表单域里的value属性值,采用这种编码方式的表单会将表单域的值处理成URL编码方式。
2) multipart/form-data:这种编码方式的表单会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数里。
3) text/plain:这种方式主要适用于直接通过表单发送邮件的方式。
文件上传是web应用经常用到的一个知识。原理是,通过为表单元素设置enctype=”multipart/form-data”属性,让表单提交的数据以二进制编码的方式提交,在接收此请求的Servlet中用二进制流来获取内容,就可以取得上传文件的内容,从而实现文件的上传。
在Java领域中,有两个常用的文件上传项目:一个是Apache组织Jakarta的Common-FileUpload组件(http://commons.apache.org/fileupload/ ),另一个是Oreilly组织的COS框架(http://www.servlets.com/cos/ )。利用这两个框架都能很方便的实现文件的上传。
2. Struts2的文件上传:
Struts2并未提供自己的请求解析器,也就是就Struts2不会自己去处理multipart/form-data的请求,它需要调用其他请求解析器,将HTTP请求中的表单域解析出来。但Struts2在原有的上传解析器基础上做了进一步封装,更进一步简化了文件上传。
Struts2默认使用的是Jakarta的Common-FileUpload框架来上传文件,因此,要在web应用中增加两个Jar文件:commons-fileupload-1.2.jar和commons-io-1.3.1.jar。它在原上传框架上做了进一步封装,简化了文件上传的代码实现,取消了不同上传框架上的编程差异。
如果要改成其它的文件上传框架,可以修改struts.multipart.parser常量的值为cos/pell,默认值是jakata。并在classpath中增加相应上传组件的类库。
2.1. 步骤一:创建带上传表单域的页面
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<html>
<head>
<title>Struts2 File Upload</title>
</head>
<body>
<form action="fileUpload.action" method="POST" enctype="multipart/form-data">
文件标题:<input type="text" name="title" size="50"/><br/>
选择文件:<input type="file" name="upload" size="50"/><br/>
<input type="submit" value=" 上传 "/>
</form>
</body>
</html>
此页面特殊之处只是把表单的enctype属性设置为multipart/form-data。
2.2. 步骤二:创建处理上传请求的Action类
package org.qiujy.web.struts2;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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;
/**
*处理文件上传的Action类
*@authorqiujy
*@version1.0
*/
publicclass FileUploadAction extends ActionSupport {
privatestaticfinalintBUFFER_SIZE = 16 * 1024;
// 文件标题
private String title;
// 上传文件域对象
private File upload;
// 上传文件名
private String uploadFileName;
// 上传文件类型
private String uploadContentType;
// 保存文件的目录路径(通过依赖注入)
private String savePath;
//以下省略getter和setter......
//自己封装的一个把源文件对象复制成目标文件对象
privatestaticvoid copy(File src, File dst) {
        InputStream in = null;
        OutputStream out = null;
try {
            in = new BufferedInputStream(new FileInputStream(src), BUFFER_SIZE);
            out = new BufferedOutputStream(new FileOutputStream(dst),
BUFFER_SIZE);
byte[] buffer = newbyte[BUFFER_SIZE];
int len = 0;
while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
if (null != in) {
try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
if (null != out) {
try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
@Override
public String execute() throws Exception {
//根据服务器的文件保存地址和原文件名创建目录文件全路径
        String dstPath = ServletActionContext.getServletContext()
                                .getRealPath(this.getSavePath())
                                + "\\" + this.getUploadFileName();
        System.out.println("上传的文件的类型:"+ this.getUploadContentType());
        File dstFile = new File(dstPath);
copy(this.upload, dstFile);
returnSUCCESS;
    }
}
上面这个Action类中,提供了title和upload两个属性来分别对应页面的两个表单域属性,用来封装表单域的请求参数。
但是,值得注意的是,此Action中还有两个属性:uploadFileName和uploadContentType,这两个属性分别用于封装上传文件的文件名、文件类型。这是Struts2设计的独到之处:Strut2的Action类直接通过File类型属性直接封装了上传文件的文件内容,但这个File属性无法获取上传文件的文件名和文件类型,所以Struts2就直接将文件域中包含的上传文件名和文件类型的信息封装到uploadFileName和uploadContentType属性中,也就是说Struts2针对表单中名为xxx的文件域,在对应的Action类中使用3个属性来封装该文件域信息:
l 类型为File的xxx属性:用来封装页面文件域对应的文件内容。
l 类型为String的xxxFileName属性:用来封装该文件域对应的文件的文件名。
l 类型为String的xxxContentType属性:用来封装该文件域应用的文件的文件类型。
另外,在这个Action类中还有一个savePath属性,它的值是通过配置文件来动态设置的,这也是Strut2设计中的一个依赖注入特性的使用。
2.3. 步骤三:配置
struts.xml文件:
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd ">
<struts>
<package name ="fileUploadDemo" extends ="struts-default">
<action name ="fileUpload"
class ="org.qiujy.web.struts2.FileUploadAction">
<!-- 动态设置Action中的savePath属性的值 -->
<param name="savePath">/upload</param>
<result name ="success">/showupload.jsp</result>
</action >
</package >
</struts>
在这个文件中跟以前配置唯一不同的是给action配置了一个<param …/>元素,用来为该Action的savePath属性动态注入值。
web.xml中的配置跟以前的应用一样。说明一点:好多网络文章说Struts2上传时要在web.xml中配置一个名为ActionContextUp的过滤器,说是有一些莫名的错误,可是是我用了Struts2新版本2.0.9GA版,测了n次,没出现什么问题,所以没配置。
2.4. 运行调试:
运行前要在根目录下创建一个名为upload的文件夹,用来存放上传后的文件。
上传结果:
3. 文件类型及错误输出:
Struts2提供了一个文件上传的拦截器(名为fileUpload),通过配置这个拦截器能轻松地实现文件类型的过滤。
在上例中,若要配置上传的文件只能是一些普通的图片文件格式:image/bmp、image/png、image/gif、image/jpeg、image/jpg等,则可在struts.xml文件中按如下方式配置:
<!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.custom.i18n.resources" value="messages"/>
<package name="fileUploadDemo" extends="struts-default">
<action name="fileUpload"
class="org.qiujy.web.struts2.FileUploadAction">
<interceptor-ref name="fileUpload">
<!-- 配置允许上传的文件类型,多个用","分隔 -->
<param name="allowedTypes">
image/bmp,image/png,image/gif,image/jpeg,image/jpg
,image/x-png, image/pjpeg
</param>
<!-- 配置允许上传的文件大小,单位字节 -->
<param name="maximumSize">102400</param>
</interceptor-ref>
<interceptor-ref name="defaultStack" />
<!-- 动态设置Action中的savePath属性的值 -->
<param name="savePath">/upload</param>
<result name="input">/index.jsp</result>
<result name="success">/showupload.jsp</result>
</action>
</package>
</struts>
如果上传文件失败,系统返回到input对应的页面,要在input对应的页面输出文件过滤失败信息,可以在input对应的页面中增加 <s:fielderror/>来显示错误信息。
运行调试:
结果:
显然,这样的提示不太友好,应用使用国际化信息。在国际化资源文件中添加如下三句:
#更改上传文件类型不允许的提示信息
struts.messages.error.content.type.not.allowed=文件上传失败:你要上传的文件类型不允许
#更改上传文件太大的提示信息
struts.messages.error.file.too.large=文件上传失败:你要上传的文件太大
#文件上传其它错误信息
struts.messages.error.uploading=文件上传失败:发生内部错误
别忘了要用native2ascii.exe进行编码转换哦。再运行调试:
另外,在控制台会看到一条消息:
Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir
Removing file upload D:\tomcat6.0.13\work\Catalina\localhost\fileload_struts2\upload__4b616fd1_115a3d5d9dc__7fff_00000005.tmp
第一个说是找不以struts.multipart.saveDir,即没有指定临时文件夹,这个很好解决,只需指定一个struts.multipart.saveDir常量值为某个目录来解决。第二个说正在删除一个临时文件,这个临时文件是上传过程中产生的,属正常。
4. 多文件上传:
Struts2也可以很方便地实现多文件上传。
在输入表单域增加多个文件域:multifileupload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>多文件上传</title>
</head>
<body>
<font color="red"><s:fielderror/></font>
<form action="multiFileUpload.action" method="POST" enctype="multipart/form-data">
文件标题:<input type="text" name="title" size="50" value="${param.title }"/><br/>
<!-- 设置二个文件域,名字相同 -->
选择第一个文件:<input type="file" name="upload" size="50"/><br/>
选择第二个文件:<input type="file" name="upload" size="50"/><br/>
<input type="submit" value=" 上传 "/>
</form>
</body>
</html>
在Action类中用数组来封装该多个文件域:MultiFileUploadAction.java
package org.qiujy.web.struts2;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
*处理多文件上传的Action类
*
*@authorqiujy
*@version1.0
*/
publicclass MultiFileUploadAction extends ActionSupport {
privatestaticfinalintBUFFER_SIZE = 16 * 1024;
// 文件标题
private String title;
// 用File数组来封装多个上传文件域对象
private File[] upload;
// 用String数组来封装多个上传文件名
private String[] uploadFileName;
// 用String数组来封装多个上传文件类型
private String[] uploadContentType;
// 保存文件的目录路径(通过依赖注入)
private String savePath;
//以下为所有属性的getter和setter。省略。。。
// 自己封装的一个把源文件对象复制成目标文件对象
privatestaticvoid copy(File src, File dst) {
        InputStream in = null;
        OutputStream out = null;
try {
            in = new BufferedInputStream(new FileInputStream(src), BUFFER_SIZE);
            out = new BufferedOutputStream(new FileOutputStream(dst),
BUFFER_SIZE);
byte[] buffer = newbyte[BUFFER_SIZE];
int len = 0;
while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
if (null != in) {
try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
if (null != out) {
try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
@Override
public String execute() throws Exception {
        File[] srcFiles = this.getUpload();
// 处理每个要上传的文件
for (int i = 0; i < srcFiles.length; i++) {
// 根据服务器的文件保存地址和原文件名创建目录文件全路径
            String dstPath = ServletActionContext.getServletContext()
                    .getRealPath(this.getSavePath())
                    + "\\" + this.getUploadFileName()[i];
            File dstFile = new File(dstPath);
this.copy(srcFiles[i], dstFile);
        }
returnSUCCESS;
    }
}
运行结果:
5. Struts2的文件下载:
文件下载相对简单一些,一般只需在页面上提供一个超链接,该链接的href属性等于要下载文件的文件名就行了。但当文件名有中文时,就会导致失败;或者要在用户下载前进行权限判断,这时用Struts2提供的文件下载功能就能简单的解决这些问题。

Jul 22
Jul 22

SSH

admin , 18:27 , 脚本JQuery » 编程(Java-Struts) , 评论(0) , 引用(0) , 阅读(1011) , Via 本站原创
分页: 1/2 第一页 1 2 下页 最后页 [ 显示模式: 摘要 | 列表 ]