[Flex]五分钟用Flex构建WebService应用

mikel阅读(745)

不管我们学习任何语言,第一个先接触到的是Hello World。然后才是其他方面的知识和技巧。学习Flex,你一定也接触过Hello World之类的起始应用,然后就是你可以开始接触MXML和AS3语法,控件,布局等知识。今天这个例子,是提供给已经开始学习Flex的开发人员,目 的是说我们可以通过Flex Builder,在五分钟之内,构造一个连接WebService服务,获取国内城市的天气预报应用。
在了解这个应用之前,请你先做好以下准备(其实也是开发其他Flex应用的准备):
下载Flex Builder 3并安装好它。你可以安装for eclispe插件的版本,也可以安装standalone的版本。安装后的使用并没有什么太大的差别。
接下来就让我们看看如何在五分钟之内完成一个Flex城市天气预报小应用,我想这应该比写HelloWorld更有成就感。
首先,我们会先使用已经准备好的一个天气预报的WebService服务,WebService服务来自WebXML,免费,而且查询信息来自国家气象局,地址如下:
http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl
你可以试着在浏览器内打开上面的这个连接,你会看到浏览器内返回的是XML结构的WSDL文档描述
从中,你可以找到这样的一部分代码:

<s:element name="getWeatherbyCityName">
  <s:complexType>
    <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="theCityName" type="s:string"/>
    </s:sequence>
  </s:complexType>
</s:element>

其 中getWeatherbyCityName就是请求WS返回特定城市天气预报的函数名称,而theCityName就是城市名的参数(其实这个参数你只 要将合法的城市名以String格式传给getWeatherbyCityName就可以了。至于哪个城市在它WebService的服务之列,你可以自 己来挖掘WebService发现
截止做完上面我说的这一步,你大概花了30秒的时间
接下来,你可以通过 访问这里去进一步测试此WebService,而且可以在着手写代码前,先看看城市天气预报返回的XML数据构成,以及SOAP1.1和1.2的标准请求 和响应示例,包括HTTP的GET和POST示例。你试着,输入城市为“北京”,你将会得到的返回数据是基于XML格式的数据
http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?op=getWeatherbyCityName

<ArrayOfString>
<string>直辖市</string>
<string>北京</string>
<string>54511</string>
<string>54511.jpg</string>
<string>2009-2-20 15:14:50</string>
<string>4℃ / -6℃</string>
<string>2月20日 晴转多云</string>
<string>微风</string>
<string>0.gif</string>
<string>1.gif</string>
<string>今日天气实况:;1℃;风向/风力:南风3级;空气质量:中;紫外线强度:弱</string>
<string>
穿衣指数:天气冷,建议着棉衣、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣或冬大衣。
感冒指数:昼夜温差较大,较易发生感冒,请适当增减衣服。体质较弱的朋友请注意防护。
晨练指数:天气晴朗,空气清新,是您晨练的大好时机,建议不同年龄段的人们积极参加户外健身活动。
交通指数:天气晴朗,路面干燥,交通气象条件良好,车辆可以正常行驶。
中暑指数:温度不高,其他各项气象条件适宜,中暑机率极低。
公园指数:天气寒冷,不适宜放风筝。
防晒指数:紫外线强度较弱,建议涂擦SPF在12-15之间,PA+的防晒护肤品。
旅行指数:晴天,天冷加上有风,旅游指数一般,您在外出游玩的时候可要多穿衣服,以免感冒呀!
</string>
<string>7℃ / -3℃</string>
<string>2月21日 多云</string>
<string>微风</string>
<string>1.gif</string>
<string>1.gif</string>
<string>4℃ / -6℃</string>
<string>2月22日 多云</string>
<string>微风</string>
<string>1.gif</string>
<string>1.gif</string>
<string>
北 京位于华北平原西北边缘,市中心位于北纬39度,东经116度,四周被河北省围着,东南和天津市相接。全市面积一万六千多平方公里,辖12区6县,人口 1100余万。北京为暖温带半湿润大陆性季风气候,夏季炎热多雨,冬季寒冷干燥,春、秋短促,年平均气温10-12摄氏度。
北京是世界历史文化名 城和古都之一。早在七十万年前,北京周口店地区就出现了原始人群部落“北京人”。而北京建城也已有两千多年的历史,最初见于记载的名字为“蓟”。公元前 1045年北京成为蓟、燕等诸侯国的都城;公元前221年秦始皇统一中国以来,北京一直是中国北方重镇和地方中心;自公元938年以来,北京又先后成为辽 陪都、金上都、元大都、明清国都。1949年10月1日正式定为中华人民共和国首都。
北京具有丰富的旅游资源,对外开放的旅游景点达200多处, 有世界上最大的皇宫紫禁城、祭天神庙天坛、皇家花园北海、皇家园林颐和园,还有八达岭、慕田峪、司马台长城以及世界上最大的四合院恭王府等各胜古迹。全市 共有文物古迹7309项,其中国家文物保护单位42个,市级文物保护单位222个。北京的市树为国槐和侧柏,市花为月季和菊花。另外,北京出产的象牙雕 刻、玉器雕刻、景泰蓝、地毯等传统手工艺品驰誉世界。
</string>
</ArrayOfString>

现 在,你看到了这就是北京近三天的天气预报和北京这个城市的基本信息(来自国家气象局)。作为一名开发者,你会发现这个WebService返回的数据是 XML结构,但是各个元素的名称却都是string,没有其他特征,这种结构最适合通过Flex的ArrayCollection来承载,而不是e4x。
到这一步,你大概花了一分钟来了解WebService了
接 下来,你要开始使用Flex Builder开发一个非常简单的天气预报查询的小应用。我们这里所实现的就是简单的查询,获取WebService结果,显示出来。我们构造这个 Flex应用,首先,我们需要使用ArrayCollection,需要使用WebService类,需要处理响应,同时,需要有个简单的错误响应。有了 这些想法,我们开始着手写代码,首先在应用场景Application上加入三个标签,一个TextInput,用来输入要查询的城市;一个按钮,用来点 击时候调用WebService服务;一个Text文本区域,用来显示返回的天气预报结果。代码也非常简单:

<mx:TextInput id="cityname" text="北京"/>
<mx:Button label="Call WebService" click=""/>
<mx:TextArea id="myWeatherReport" height="100%" width="80%" editable="false" />

到了这里,你又花了一分钟的时间。
接下来,你要引入WebService服务,你需要加入一个WebService标签来帮你完成任务

<mx:WebService id="webService" wsdl="http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl"
showBusyCursor="true" result="onLoad(event)" fault="faultHandler(event)"/>

这 里有几个属性,实例化id是webService,wsdl地址就是我们上面的那个wsdl地址,showBusyCursor设置为true,表示工作 状态,result事件就是当WebService返回结果时,调用一个方法,这个方法,我们叫做onLoade,传递的参数是result event对象,同样,fault事件的响应函数将是faultHandler。然后,我们给Button控件的click事件加入对于 WebService的调用"webService.getWeatherbyCityName(cityname.text)"。
到了这里,你又花了大概一分钟的时间。
最后,引入我们对于Onload和faultHandler的处理,也很简单:

  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      import mx.utils.ArrayUtil;
      import mx.controls.Alert;
      import mx.rpc.events.ResultEvent;
      import mx.rpc.events.FaultEvent;
      private function onLoad(event:ResultEvent):void{
        //Alert.show(event.result.toString(),"WebService Results");
        var weatherInfo:ArrayCollection = (event.result) as ArrayCollection;
        for(var i:int=0;i<weatherInfo.length;i++){
          myWeatherReport.text+=weatherInfo[i]+"\n";
        }
      }
      private function faultHandler(event:FaultEvent):void{
        Alert.show(event.fault.toString(),'WebService Error');
      }
    ]]>
  </mx:Script>

截止到这里,你可能又花了大概一分半钟,总共已经花掉了四分半钟的时间。
最后30秒,留给你测试这个应用。

怎么样,是不是比做HelloWorld还有成就感?你还可以通过CSS和其他各种方式进一步美化应用,达到很酷的设计要求。
当然,我这五分钟只是一个“噱头”,真正要说的意思,就是Flex开发上手和学习的确非常简单,尤其是对于已经有Java或.net开发经验的朋友,用Flex来制作一个小的应用,更是高效快捷!

[C#]公交车路线查询系统后台数据库设计系列文章

mikel阅读(909)

在《查询算法》和《关联地名和站点》两篇文章中,已经实现了通过地名或站点进行路线查询的算法,但是在现实中,从起点到终点不一定全程都是乘车,例如,有以下3条路线:

R1: S1->S2->S3->S4->S5

R2: S6->S7->S2->S8

R3: S8->S9->S10

假如现在要从站点S1S7,如果用Inquiry查询路线,显然没有合适的乘车方案。但是S2S7相距仅仅一个站的距离,可以用步行代替,因此可以先S1乘坐R1S2再步行到S7

为了实现在乘车路线中插入步行路线,在数据库使用WalkRoute(StartStop, EndStop, Distance, Remark)(StartStop-起始站点,EndStop-目的站点,Distance-距离,Remark-备注)储存距离较近的两个站点。

加入表WalkRoute后,查询算法也要作相应的修改,其实WalkRouteRouteT0很相似,因此只需把WalkRoute看成是特殊的直达线路即可,修改后的InqueryT1如下: 


/*
查询站点@StartStops到站点@EndStops之间的一次换乘乘车路线,多个站点用'/'分开,如:
exec InquiryT1 '站点1/站点2','站点3/站点4'
*/
Create proc InquiryT1(@StartStops varchar(32),@EndStops varchar(32))
as
begin
    
declare @ss_tab table(name varchar(32))
    
declare @es_tab table(name varchar(32))
    
insert @ss_tab select Value from dbo.SplitString(@StartStops,'/')
    
insert @es_tab select Value from dbo.SplitString(@EndStops,'/')
    
if(exists(select * from @ss_tab sst,@es_tab est where sst.name=est.name))
    
begin
        
raiserror ('起点集和终点集中含有相同的站点',16,1)
        
return
    
end
    
declare @stops table(name varchar(32))
    
insert @stops select name from @ss_tab
    
insert @stops select name from @es_tab
    
declare @result table(
        StartStop 
varchar(32),
        Route1 
varchar(256),
        TransStop 
varchar(32),
        Route2 
varchar(256),
        EndStop 
varchar(32),
        StopCount 
int
    )
    
declare @count int
    
set @count=0
    
查询"步行-乘车"路线
    insert @result
    
select 
        sst.name 
as StartStop,
        
''+r1.StartStop+'步行到'+r1.EndStop as Route1,
        r1.EndStop 
as TransStop,
        r2.Route 
as Route2,
        est.name 
as EndStop,
        r2.StopCount 
as StopCount
    
from 
        
@ss_tab sst,
        
@es_tab est,
        (
select * from WalkRoute where EndStop not in (select name from @stops)) r1,
        RouteT0 r2
    
where
        sst.name
=r1.StartStop
        
and r1.EndStop=r2.StartStop
        
and r2.EndStop=est.name
    
order by r2.StopCount
    
set @count=@@rowcount
    
查询"乘车-步行"路线
    insert @result
    
select 
        sst.name 
as StartStop,
        r1.Route 
as Route1,
        r1.EndStop 
as TransStop,
        
''+r2.StartStop+'步行到'+r2.EndStop as Route2,
        est.name 
as EndStop,
        r1.StopCount 
as StopCount
    
from 
        
@ss_tab sst,
        
@es_tab est,
        RouteT0 r1,
        (
select * from WalkRoute where StartStop not in (select name from @stops)) r2
    
where
        sst.name
=r1.StartStop
        
and r1.EndStop=r2.StartStop
        
and r2.EndStop=est.name
    
order by r1.StopCount
    
set @count=@count+@@rowcount
    
    
if(@count=0)
    
begin
        
查询"乘车-乘车"路线
        insert @result
        
select
            sst.name 
as StartStop,
            r1.Route 
as Route1,
            r1.EndStop 
as TransStop,
            r2.Route 
as Route2,
            est.name 
as EndStop,
            r1.StopCount
+r2.StopCount as StopCount
        
from 
            
@ss_tab sst,
            
@es_tab est,
            (
select * from RouteT0 where EndStop not in (select name from @stops)) r1,
            RouteT0 r2
        
where
            sst.name
=r1.StartStop
            
and r1.EndStop=r2.StartStop
            
and r2.EndStop=est.name
            
and r1.Route<>r2.Route
        
order by r1.StopCount+r2.StopCount
    
end
    
select
        StartStop 
as 起始站点,
        Route1 
as 路线1,
        TransStop 
as 中转站点,
        Route2 
as 路线2,
        EndStop 
as 目的站点,
        StopCount 
as 总站点数
    
from
        
@result
end

[Velocity]Velocity用户手册

mikel阅读(890)




Velocity用户手册

英文来源: http://jakarta.apache.org/velocity/docs/user-guide.html

关于这个用户手册

Velocity用户手册是帮助页面设计者和内容提供者认识Velocity和其简单而功能强大的脚本语言――Velocity模板语言(VTL)。在手册上的许多例子,都是用Velocity插入动态的内容到网页上,但是所有的VLT例子都能应用到其他的页面和模板中。

感谢使用 Velocity

Velocity是什么?

Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。

Velocity应用于web开发时,界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只关注页面的显示效果,而由java程序开发人员关注业务逻辑编码。Velocityjava代码从web页面中分离出来,这样为web站点的长期维护提供了便利,同时也为我们在JSPPHP之外又提供了一种可选的方案。

Velocity的能力远不止web站点开发这个领域,例如,它可以从模板(template)产生SQLPostScriptXML,它也可以被当作一个独立工具来产生源代码和报告,或者作为其他系统的集成组件使用。Velocity也可以为Turbine web开发架构提供模板服务(template service)。Velocity+Turbine提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。

 

Velocity能为我们作什么?

Mud 商店例子

假设你是一家专门出售Mud的在线商店的页面设计人员,让我们暂且称它为在线MUD商店。你们的业务很旺,客户下了各种类型和数量的mud订单。他们都是通过输入用户名和密码后才登陆到你的网站,登陆后就允许他们查看订单并购买更多的mud。现在,一种非常流行的mud正在打折销售。另外有一些客户规律性的购买另外一种也在打折但是不是很流行的Bright Red Mud,由于购买的人并不多所以它被安置在页面的边缘。所有用户的信息都是被跟踪并存放于数据库中的,所以某天有一个问题可能会冒出来:为什么不使用velocity来使用户更好的浏览他们感兴趣的商品呢?

Velocity使得web页面的客户化工作非常容易。作为一个web site的设计人员,你希望每个用户登陆时都拥有自己的页面。

你会见了一些公司内的软件工程师,你发现他们每个人都同意客户应该拥有具有个性化的信息。那让我们把软件工程师应该作的事情发在一边,看一看你应该作些什么吧。

你可能在页面内嵌套如下的VTL声明:

<HTML>

<BODY>

Hello $customer.Name!

<table>

#foreach( $mud in $mudsOnSpecial )

   #if ( $customer.hasPurchased($mud) )

      <tr>

        <td>

          $flogger.getPromo( $mud )

        </td>

      </tr>

   #end

#end

</table>

foreach 的详细用法不久就会进行深入描述。重要的是,这短小的脚本能在你的网站上出现。当一个对Bright Red Mud 很感兴趣的顾客登陆的时候,同时Bright Red Mud在热卖中,这时顾客就能显著地看到。假如一个玩Terracotta Mud很久的顾客登陆,Terracotta Mud 的售卖信息就会出现在前面中间。Velocity的适用性是很巨大的,限制的只是你的创造性。

     VTL Reference 含有许多其他Velocity元素,这些元素能够共同帮助你,使你的网站更加好。当你越来越熟悉那些原理,你开始释放Velocity的能力。

Velocity模板语言(VTL):说明

VTL意味着提供最简单、最容易并且最整洁的方式合并页面动态内容。VTL使用references来在web site内嵌套动态内容,一个变量就是一种类型的reference。变量是某种类型的refreence,它可以指向java代码中的定义,或者从当前页面内定义的VTL statement得到值。下面是一个VTL statement的例子,它可以被嵌套到HTML代码中:

#set( $a = "Velocity" )

和所有的VTL statement一样,这个statement以#字符开始并且包含一个directiveset。当一个在线用户请求你的页面时,Velocity 模板引擎将查询整个页面以便发现所有#字符,然后确定哪些是VTL statement,哪些不需要VTL作任何事情。

#字符后紧跟一个directiveset时,这个set directive使用一个表达式(使用括号封闭)――一个方程式分配一个值给变量。变量被列在左边,而它的值被列在右边,最后他们之间使用=号分割。

在上面的例子中,变量是$a,而它的值是Velocity。和其他的references一样以$字符开始,而值总是以双引号封闭。Velocity中仅有String可以被赋值给变量。

使用$字符开始的references用于得到什么;使用#字符开始的directives用于作些什么。在上面的例子中,#set是分配一个值给变量。变量$a 在模板中输出 "Velocity"

 

Hello Velocity World!

一旦某个变量被分配了一个值,那么你就可以在HTML文件的任何地方引用它。在下面的例子中,一个值被分配给$foo变量,并在其后被引用。

<html>

<body>

#set( $foo = "Velocity" )

Hello $foo World!

</body>

<html>

上面的实现结果是在页面上打印“Hello Velocity World

为了使包含VTL directivesstatement更具有可读性,我们鼓励你在新行开始每个VTL statement,尽管你不是必须这么作。Set的用法将在后面详细描述。

 

注释

         注释是那些描述文本不出现在模板引擎输出里面。注释一个主要用处是提醒自己和解释出现在VTL中的声明,或是其他用途。下面是一个在VLT中的注释例子。

## This is a single line comment.

单行注释以##开始,结束在这行的结尾。如果你要写几行注释,这没有必要写几个单行注释。

多行注释,以#*开始*#结束,可以解决这个问题。

This is text that is outside the multi-line comment.

Online visitors can see it.

 

#*

 Thus begins a multi-line comment. Online visitors won't

 see this text because the Velocity Templating Engine will

 ignore it.

*#

 

Here is text outside the multi-line comment; it is visible.

这里有几个例子关于单行和多行注释如何工作。

This text is visible. ## This text is not.

This text is visible.

This text is visible. #* This text, as part of a multi-line comment,

is not visible. This text is not visible; it is also part of the

multi-line comment. This text still not visible. *# This text is outside

the comment, so it is visible.

## This text is not visible.

这里有第三种类型的注释,VLT注释块,可能用于存放文档的作者名称和版本信息。

#**

This is a VTL comment block and

may be used to store such information

as the document author and versioning

information:

@author

@version 5

*#

 

References

 

VTL中有三种类型的references:变量(variables)、属性(properties)、方法(methods)。作为一个使用VTL的页面设计者,你和你的工程师必须就references的名称达成共识,以便你可以在你的template中使用它们。

所有的  reference被作为一个String对象处理。如果有一个对象$foo是一个Integer对象,那么Velocity将调用它的toString()方法将这个对象转型为String类型。

 

变量

非正式变量是由“$”开头,接着是VTL标识符。VLT标识符必须以字母(a..z,A..Z)开头。剩下的部分限于以下几种:

l          字母(a..z,A..Z)

l          数字(0..9)

l          连字符(“-”)

l          下划线(“_”)

这里是几个在VTL中有效的变量reference

$foo

$mudSlinger

$mud-slinger

$mud_slinger

$mudSlinger1

VLT定义一个变量,例如$foo,变量能或者通过模板中的set 方法,或者通过Java 代码获得值。

例如,Java 变量 $foo 的值是 bar ,在这个模板被请求时,在网页上 bar 会替代所有 $foo 。选择地,假如包括了下面的声明:

#set( $foo = "bar" )

按照这样的设置,输出就会跟之前的一样。

 

属性

第二种有趣的VTL reference就是属性,而且属性有一种与众不同的格式。非正式变量是由“$”开头,接着是VTL标识符,再接着就是字符(“.”)和其他的VLT标识符。这里是一些在VLT中有效属性的定义:

$customer.Address

$purchase.Total

在第一个例子中$customer.Address有两种含义。它可以表示:查找hashtable对象customer中以Address为关键字的值;也可以表示调用customer对象的getAddress()方法。当你的页面被请求时,Velocity将确定以上两种方式选用那种,然后返回适当的值。

 

方法

一个方法就是被定义在java中的一段代码,并且它有完成某些有用工作的能力,例如一个执行计算和判断条件是否成立、满足等。方法是一个由$开始并跟随VTL标识符组成的References,一般还包括一个VTL方法体。一个VTL方法体包括一个VLT标识接着一个左括号(“(”),接着是参数列表,再接着是右括号(“)”)。这里是一些在VTL中有效的方法定义:

$customer.getAddress()

$purchase.getTotal()

$page.setTitle( "My Home Page" )

$person.setAttributes( ["Strange", "Weird", "Excited"] )

前两个例子$customer.getAddress()$purchase.getTotal()看起来挺想上面的属性$customer.Address $purchase.Total。如果你觉得他们之间有某种联系的话,那你是正确的。

VTL属性可以作为VTL方法的缩写。$customer.Address属性和使用$customer.getAddress()方法具有相同的效果。如果可能的话使用属性的方式是比较合理的。属性和方法的不同点在于你能够给一个方法指定一个参数列表。

非正式定义能够用下面的方法:

$sun.getPlanets()

$annelid.getDirt()

$album.getPhoto()

我们期待那些方法返回属于太阳系的行星的名称,喂养我们的蚯蚓,或者从相册里面取出一张照片。只有长符号为下面的方法服务。

$sun.getPlanet( ["Earth", "Mars", "Neptune"] )

## Can't pass a parameter list with $sun.Planets

 

$sisyphus.pushRock()

## Velocity assumes I mean $sisyphus.getRock()

 

$book.setTitle( "Homage to Catalonia" )

## Can't pass a parameter list

 

正式reference标记

非正式references用于上述的例子中。但是同样有正式的references,如下面所示:

${mudSlinger}

${customer.Address}

${purchase.getTotal()}

在几乎所有场合你都可以使用非正式references,但是在某些场合,只能使用正式reference 才能正确处理。

设想你创建一个句子:$vice 作为句子的名词 。目标是为了使某些人选择不同的词,产生下面两种结果之一:"Jack is a pyromaniac." 或者 "Jack is a kleptomaniac."。使用非正式定义不太适合用于这种情况。看一下下面的例子:

Jack is a $vicemaniac.

本来变量是$vice现在却变成了$vicemaniac,这样Veloctiy就不知道您到底要什么了。所以,应该使用正是格式书写

Jack is a ${vice}maniac.

现在Velocity就知道reference $vice,而不是$vicemaniac。正式定义经常用于模板中refernces与文本连接在一起的情况。

 

Quiet reference notation

Velocity遇到没有定义的reference,通常它会直接输出reference。例如:假如下面的reference出现在一个VTL模板中:

<input type="text" name="email" value="$email"/>

form 最初加载的时候,变量$email 没有值,但你想出现一个空白的文本框设定值为“$email”。

使用quiet reference notation可以使Velocity正常显示,你需要用$!email ,代替$email。所以上面的例子,会改成下面:

<input type="text" name="email" value="$!email"/>

这样文本框的初始值就不会是email而是空值了。

正式和quiet格式的reference notation也可一同使用,像下面这样:

<input type="text" name="email" value="$!{email}"/>

 

Getting literal

Velocity使用特殊字符$#来帮助它工作,所以如果要在template里使用这些特殊字符要格外小心。本节将讨论$字符。

货币字符

这是没有问题的:"I bought a 4 lb. sack of potatoes at the farmer's market for only $2.50!"VTL中使用$2.5这样的货币标识是没有问题得的,VTL不会将它错认为是一个reference,因为VTL中的reference总是以一个大写或者小写的字母开始。

 

Escaping valid VTL reference

某些情况使用Velocity可能会觉得很烦恼。逃避特殊符是处理出现在你的模板中VTL特殊符有效方法,就是使用反斜杠(“\”)

#set( $email = "foo" )

$email

假如Velocity在你的模板中遇到 $email ,它会搜索上下文,得到相应的值。这里的输出是 foo ,因为 $email 被定义了。假如  $email 没有被定义,输出会是 $email

设想 $email 被定义了(例如,它的值是 foo ),而且你想输出 $email 。这里有几种方法能达到目的,但是最简单的是使用逃避符。

## The following line defines $email in this template:

#set( $email = "foo" )

$email

\$email

\\$email

\\\$email

将显示为:

foo

$email

\foo

\$email

注意到 \ 屏蔽了左边的 $”。屏蔽左边规则,使得 \\\$email 显示为 \\$email 。那些例子与 $email没有定义相比较。

$email

\$email

\\$email

\\\$email

将显示为:

$email

\$email

\\$email

\\\$email

注意Velocity处理 定义了的references 与没有定义的不一样。这里 set $foo 的值为 gibbous

#set( $foo = "gibbous" )

$moon = $foo

输出会是:$moon = gibbous ,$moon 按照字面上输出因为它没有定义,gibbous 替代 $foo 输出。

避开VTL directives 还有其他方法,在 Directives 那章节会更详细描述。

 

Case substitution

现在你已经对reference比较熟悉了,你可以将他们高效的应用于你的template了。Velocity利用了很多java规范以方便了设计人员的使用。例如:

$foo

 

$foo.getBar()

## is the same as

$foo.Bar

 

$data.getUser("jon")

## is the same as

$data.User("jon")

 

$data.getRequest().getServerName()

## is the same as

$data.Request.ServerName

## is the same as

${data.Request.ServerName}

那些例子说明了同样的references 用法。Velocity 利用Java instrospection bean features解决 reference 的名称对于对象和对象方法的问题。它可以出入你的模板中和求出 references 的值。

Velocity 是模仿Sun微系统中的Bean规范定义的,因而它是很灵活的。然而,它的开发者已经很努力地捕捉和纠正可能出现的错误。当方法 getFoo() 在模板中 $bar.foo调用时,Velocity首先尝试 $getfoo。如果失败,它会继续尝试 $getFoo。同样地,当模板查询 $bar.FooVelocity会先尝试 $getFoo(),然后再尝试 $getfoo()

但是,注意VTL中不会将reference解释为对象的实例变量。例如:$foo.Name将被解释为Foo对象的getName()方法,而不是Foo对象的Name实例变量。

 

Directives

Reference允许设计者使用动态的内容,而directive使得你可以应用java代码来控制你的显示逻辑,从而达到你所期望的显示效果。

#set

#set标志是用于对一个reference赋值。值会赋给一个变量或者一个属性,而且赋值会在括号里出现:

#set( $primate = "monkey" )

#set( $customer.Behavior = $primate )

左边(LHS) 一定是一个变量或者一个属性。右边(RHS)可以是下面中的一个类型:

l          变量

l          字符串

l          属性

l          方法

l          数字

l          数组

这些例子显示上述的每一种类型:

#set( $monkey = $bill ) ## variable reference

#set( $monkey.Friend = "monica" ) ## string literal

#set( $monkey.Blame = $whitehouse.Leak ) ## property reference

#set( $monkey.Plan = $spindoctor.weave($web) ) ## method reference

#set( $monkey.Number = 123 ) ##number literal

#set( $monkey.Say = ["Not", $my, "fault"] ) ## ArrayList

注意:最后一个例子的取值方法为:$monkey.Say.get(0)

RHS也可以是一个简单的算术表达式:

#set( $value = $foo + 1 )

#set( $value = $bar – 1 )

#set( $value = $foo * $bar )

#set( $value = $foo / $bar )

如果你的RHS是一个nullVTL的处理将比较特殊:它将指向一个已经存在的reference,这对初学者来讲可能是比较费解的。例如:

#set( $result = $query.criteria("name") )

The result of the first query is $result

 

#set( $result = $query.criteria("address") )

The result of the second query is $result

如果$query.criteria(“name”)返回一个“bill”,而$query.criteria(“address”)返回的是null,则显示的结果如下:

The result of the first query is bill

 

The result of the second query is bill

这容易使新手糊涂:创建一个#foreach循环,企图想通过一个属性或者一个方法 #set一个reference,然后马上就用 #if 测试。例如:

#set( $criteria = ["name", "address"] )

 

#foreach( $criterion in $criteria )

 

    #set( $result = $query.criteria($criterion) )

 

    #if( $result )

        Query was successful

    #end

 

#end

在上面的例子中,程序将不能智能的根据$result的值决定查询是否成功。在$result#set后(added to the context),它不能被设置回nullremoved from the context)。打印的结果将显示两次查询结果都成功了,但是实际上有一个查询是失败的。

为了解决以上问题我们可以通过预先定义的方式:

#set( $criteria = ["name", "address"] )

 

#foreach( $criterion in $criteria )

 

    #set( $result = false )

    #set( $result = $query.criteria($criterion) )

 

    #if( $result )

        Query was successful

    #end

 

#end

不像其他Velocity指示符号,#set 没有一个#end 结束。

 

String Literals

当你使用#set directiveString literal封闭在一对双引号内。象下面:

#set( $directoryRoot = "www" )

#set( $templateName = "index.vm" )

#set( $template = "$directoryRoot/$templateName" )

$template

上面这段代码的输出结果为:

www/index.vm

但是,当string literal被封装在单引号内时,它将不被解析:

#set( $foo = "bar" )

$foo

#set( $blargh = '$foo' )

$blargh

输出为:

bar $foo

上面这个特性可以通过修改velocity.properties文件的stringliterals.interpolate = false的值来改变上面的特性是否有效。

 

条件语句

If / ElseIf / Else

当一个web页面被生成时使用Velocity#if directrive,如果条件成立的话可以在页面内嵌入文字。例如:

#if( $foo )

   <strong>Velocity!</strong>

#end

上例中的条件语句将在以下两种条件下成立:(i)$foo是一个boolean型的变量,且它的值为true(ii)$foo变量的值不为null。这里需要注意一点:Velocity context仅仅能够包含对象,所以当我们说“boolean”时实际上代表的时一个Boolean对象。即便某个方法返回的是一个boolean值,Velocity也会利用内省机制将它转换为一个Boolean的相同值。

如果条件成立,那么#if#end之间的内容将被显示。在这个例子中,如果 $foo的值为true,输出为“Velocity!”。相反地,如果 $foo 是一个 null值,或者是一个 false 值,表达式值为 false,没有输出。

#elseif#else元素可以同#if一同使用。注意:Velocity 模板引擎遇到一个为true值的表达式就会停止。在下面的例子,假设 $foo=15,$bar=6

#if( $foo < 10 )

    <strong>Go North</strong>

#elseif( $foo == 10 )

    <strong>Go East</strong>

#elseif( $bar == 6 )

    <strong>Go South</strong>

#else

    <strong>Go West</strong>

#end

在这个例子中,$foo10大,所以在开始的两个比较中都失败。接着 $bar6比较是真的所以输出为 GO South

注意这里的Velocity的数字是作为Integer来比较的――其他类型的对象将使得条件为false,但是与java不同它使用==来比较两个值,而且velocity要求等号两边的值类型相同。

 

关系、逻辑运算符

Velocity中使用等号操作符判断两个变量的关系。这里有个简单例子关于等于号的使用:

#set ($foo = "deoxyribonucleic acid")

#set ($bar = "ribonucleic acid")

 

#if ($foo == $bar)

  In this case it's clear they aren't equivalent. So…

#else

  They are not equivalent and this will be the output.

#end

VelocityANDORNOT逻辑运算符。想得到更多信息,请看VTL Reference Guide。下面的例子是说明ANDORNOT逻辑运算符的用法:

## logical AND

 

#if( $foo && $bar )

   <strong> This AND that</strong>

#end

只有当$foo $bar 都为true#if 才会得到true值。如果$foofalse ,表达式的值为 false$bar 就不会求值。如果 $foo的值为 true Velocity模板引擎会检查 $bar的值,如果$bar=true,整个表达式的值为true,输出为“This AND that”。如果$bar=false,整个表达式的值为false,没有输出。

逻辑OR 的工作方式一样,除了只要有一个值为true ,整个表达式的值就为true。考虑一下下面的例子。

## logical or

 

#if( $foo || $bar )

    <strong>This or That</strong>

#end

如果$footrueVelocity模板引擎就没有必要查找 $bar,无论 $bar true 还是 false ,表达式的值为 true ,输出为“This or That”。如果$fooflase$bar的值就一定要检查,在这个例子中,如果 $bar 同样是false,表达式的值为 false ,没有输出。从另外一个角度看,如果$bar 的值为true ,整个表达式的值为 true ,输出为 This or That”。

关于逻辑NOT ,只有一个疑问:

##logical NOT

 

#if( !$foo )

  <strong>NOT that</strong>

#end

如果 $foo=true, !$foo 的值为 false,没有输出。如果$foo=false!$foo的值为true,输出为“NOT that”。注意不要跟 quiet reference $!foo 混为一谈,那是完全不一样的。

 

循环

Foreach循环

#foreach 用于循环。例子:

<ul>

#foreach( $product in $allProducts )

    <li>$product</li>

#end

</ul>

每次循环$allProducts中的一个值都会赋给$product变量。

$allProducts可以是一个VectorHashtable或者Array。分配给$product的值是一个java对象,并且可以通过变量被引用。例如:如果$product是一个javaProduct类,并且这个产品的名字可以通过调用他的getName()方法得到。

现在我们假设$allProducts是一个Hashtable,如果你希望得到它的key应该像下面这样:

<ul>

#foreach( $key in $allProducts.keySet() )

    <li>Key: $key -> Value: $allProducts.get($key)</li>

#end

</ul>

Velocity还特别提供了得到循环次数的方法,以便你可以像下面这样作:

<table>

#foreach( $customer in $customerList )

    <tr><td>$velocityCount</td><td>$customer.Name</td></tr>

#end

</table>

$velocityCount变量的名字是Velocity默认的名字,你也可以通过修改velocity.properties文件来改变它。默认情况下,计数从1”开始,但是你可以在velocity.properties设置它是从1”还是从0”开始。下面就是文件中的配置:

# Default name of the loop counter

# variable reference.

directive.foreach.counter.name = velocityCount

 

# Default starting value of the loop

# counter variable reference.

directive.foreach.counter.initial.value = 1

 

include

#include script element允许模板设计者引入本地文件。被引入文件的内容将不会通过模板引擎被render。为了安全的原因,被引入的本地文件只能在TEMPLATE_ROOT目录下。

#include( "one.txt" )

#include 引用的文件用引号括起来。

如果您需要引入多个文件,可以用逗号分隔就行:

#include( "one.gif","two.txt","three.htm" )

在括号内可以是文件名,但是更多的时候是使用变量的。这用于根据页面提交的需求而输出。这里有一个例子同时有文件名和变量。

#include( "greetings.txt", $seasonalstock )

 

parse

#parse script element允许模板设计者一个包含VTL的本地文件。Velocity将解析其中的VTLrender模板。

#parse( "me.vm" )

就像#include#parse接受一个变量而不是一个模板。任何由#parse指向的模板都必须包含在TEMPLATE_ROOT目录下。与#include不同的是,#parse只能指定单个对象。

你可以通过修改velocity.properties文件的parse_direcive.maxdepth的值来控制一个template可以包含的最多#parse的个数――默认值是10#parse是可以递归调用的,例如:如果dofoo.vm包含如下行:

Count down.

#set( $count = 8 )

#parse( "parsefoo.vm" )

All done with dofoo.vm!

那么在parsefoo.vm模板中,你可以包含如下VTL

$count

#set( $count = $count – 1 )

#if( $count > 0 )

    #parse( "parsefoo.vm" )

#else

    All done with parsefoo.vm!

#end

在显示“Count down”后,Velocity通过parsefoo.vm,从8往下数。当计数到了0,它就会显示“All done with parsefoo.vm!”。在这时,Velocity 会返回到dofoo.vm ,输出信息:“All done with dofoo.vm!”。

 

 

停止

#stop script element允许模板设计者停止执行模板引擎并返回。把它应用于Debug是很有帮助的。

#stop

 

 

Velocimacros

#macro script element允许模板设计者定义一段可重用的VTL templateVelocimacros广泛用于简单和复杂的行列。Velocimacros的出现是为了减少编码和极小化排版错误,对Velocimacros的概念提供一个介绍。

#macro( d )

<tr><td></td></tr>

#end

在上面的例子中Velocimacro被定义为d,然后你就可以在任何VTL directive中以如下方式调用它:

#d()

当你的template被调用时,Velocity将用<tr><td></td></tr>替换为#d()

每个Velocimacro可以拥有任意数量的参数――甚至0个参数,虽然定义时可以随意设置参数数量,但是调用这个Velocimacro时必须指定正确的参数。下面是一个拥有两个参数的Velocimacro,一个参数是color另一个参数是array

#macro( tablerows $color $somelist )

#foreach( $something in $somelist )

    <tr><td bgcolor=$color>$something</td></tr>

#end

#end

Velocimacro在这个例子的定义,tablerows,有两个元素。第一个是替换 $color ,第二个是替换$somelist

任何东西都可以通过Velocimacro加入到VTL模板中。tablerows Velocimacro 是一个foreach 标识。 这里有两个#end标识在#tablerows Velocimacro 定义里面。第一个是属于 #foreach,第二个是属于Velocimacro定义的。

#set( $greatlakes = ["Superior","Michigan","Huron","Erie","Ontario"] )

#set( $color = "blue" )

<table>

    #tablerows( $color $greatlakes )

</table>

注意到$greatlakes 替代 $somelist。当#tablerows Velocimacro被调用时,就会产生下面的输出:

<table>

    <tr><td bgcolor="blue">Superior</td></tr>

    <tr><td bgcolor="blue">Michigan</td></tr>

    <tr><td bgcolor="blue">Huron</td></tr>

    <tr><td bgcolor="blue">Erie</td></tr>

    <tr><td bgcolor="blue">Ontario</td></tr>

</table>

Velocimacros可以在Velocity模板内实现行内定义(inline),也就意味着同一个web site内的其他Velocity模板不可以获得Velocimacros的定义。定义一个可以被所有模板共享的Velocimacro显然是有很多好处的:它减少了在一大堆模板中重复定义的数量、节省了工作时间、减少了出错的几率、保证了单点修改。

上面定义的#tablerows( $color $list )Velocimacro被定义在一个Velocimacros模板库(velocity.properties中定义)里,所以这个macro可以在任何规范的模板中被调用。它可以被多次应用并且可以应用于不同的目的。例如下面的调用:

#set( $parts = ["volva","stipe","annulus","gills","pileus"] )

#set( $cellbgcol = "#CC00FF" )

<table>

#tablerows( $cellbgcol $parts )

</table>

当为mushroom.vm实现一个请求时,Velocity会在模板库中找到#tablerows Velocimacro(定义在velocity.properties文件),产生以下输出:

<table>

    <tr><td bgcolor="#CC00FF">volva</td></tr>

    <tr><td bgcolor="#CC00FF">stipe</td></tr>

    <tr><td bgcolor="#CC00FF">annulus</td></tr>

    <tr><td bgcolor="#CC00FF">gills</td></tr>

    <tr><td bgcolor="#CC00FF">pileus</td></tr>

</table>

 

Velocimacro arguments

Velocimacro可以使用以下任何元素作为参数:

l          Reference:任何以$开头的reference

l          String literal

l          Number literal

l          IntegerRange[1….3]或者[$foo….$bar]

l          对象数组:[“a”,”b”,”c”]

l          boolean值:truefalse

当将一个reference作为参数传递给Velocimacro时,请注意reference作为参数时是以名字的形式传递的。这就意味着参数的值在每次Velocimacro内执行时才会被产生。这个特性使得你可以将一个方法调用作为参数传递给Velocimacro,而每次Velocimacro执行时都是通过这个方法调用产生不同的值来执行的。例如:

     #macro( callme $a )

         $a $a $a

     #end

     #callme( $foo.bar() )

执行的结果是:reference $foobar()方法被执行了三次。

         在第一次扫描中,出现惊人的作用。但是当你考虑到Velocimacro的最原始动机:消除剪切、粘贴、复制,简单地使用VTL使它更灵活。它允许你对传递对象到Velocimacro,例如一个产生重复序列颜色,着色表格的行的对象。

如果你不需要这样的特性可以通过以下方法:

#set( $myval = $foo.bar() )

#callme( $myval )

 

Velocimacro properties

Velocity.properties文件中的某几行能够使Velocimacros的实现更加灵活。注意更多的内容可以看Developer Guide

Velocity.properties文件中的velocimacro.libraary:一个以逗号分隔的模板库列表。默认情况下,velocity查找唯一的一个库:VM_global_library.vm。你可以通过配置这个属性来指定自己的模板库。

         Velocity.properties文件中的velocimacro.permissions.allow.inline属性:有两个可选的值true或者false,通过它可以确定Velocimacros是否可以被定义在regular template内。默认值是ture――允许设计者在他们自己的模板中定义Velocimacros

         Velocity.properties文件中的velocimacro.permissions.allow.inline.replace.global属性有两个可选值truefalse,这个属性允许使用者确定inlineVelocimacro定义是否可以替代全局Velocimacro定义(比如在velocimacro.library属性中指定的文件内定义的Velocimacro)。默认情况下,此值为false。这样就阻止本地Velocimacro定义覆盖全局定义。

         Velocity.properties文件中的velocimacro.permissions.allow.inline.local.scale属性也是有truefalse两个可选值,默认是false。它的作用是用于确定你inline定义的Velocimacros是否仅仅在被定义的template内可见。换句话说,如果这个属性设置为true,一个inline定义的Velocimacros只能在定义它的template内使用。你可以使用此设置实现一个奇妙的VM窍门:a template can define a private implementation of the second VM that will be called by the first VM when invoked by that template. All other templates are unaffected

Velocity.properties文件中的velocimacro.context.localscope属性有truefalse两个可选值,默认值为false。当设置为true时,任何在Velocimacro内通过#set()context的修改被认为是针对此velocimacro的本地设置,而不会永久的影响内容。

         Velocity.properties文件中的velocimacro.library.autoreload属性控制Velocimacro库的自动加载。默认是false。当设置为ture时,对于一个Velocimacro的调用将自动检查原始库是否发生了变化,如果变化将重新加载它。这个属性使得你可以不用重新启动servlet容器而达到重新加载的效果,就像你使用regular模板一样。这个属性可以使用的前提就是resource loader缓存是off状态(file.resource.loader.cache = false)。注意这个属性实际上是针对开发而非产品的。

 

Velocimacro Trivia

Velocimacro必须被定义在他们被使用之前。也就是说,你的#macro()声明应该出现在使用Velocimacros之前。

特别要注意的是,如果你试图#parse()一个包含#macro()的模板。因为#parse()发生在运行期,但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素,这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果,只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs

 

Escaping VTL directives

VTL directives can be escaped with “\”号,使用方式跟VTLreference使用逃逸符的格式差不多。

## #include( "a.txt" ) renders as <contents of a.txt>

#include( "a.txt" )

 

## \#include( "a.txt" ) renders as \#include( "a.txt" )

\#include( "a.txt" )

 

## \\#include ( "a.txt" ) renders as \<contents of a.txt>

\\#include ( "a.txt" )

在对在一个directive内包含多个script元素的VTL directives使用逃逸符时要特别小心(比如在一个if-else-end statement内)。下面是VTLif-statement的典型应用:

#if( $jazz )

    Vyacheslav Ganelin

#end

如果$jazzture,输出将是:

Vyacheslav Ganelin

如果$jazzfalse,将没有输出。使用逃逸符将改变输出。考虑一下下面的情况:

\#if( $jazz )

    Vyacheslav Ganelin

\#end

现在无论$jazztrue还是false,输出结果都是:

#if($ jazz )

     Vyacheslav Ganelin

 #end

事实上,由于你使用了逃逸符,$jazz根本就没有被解析为boolean型值。在逃逸符前使用逃逸符是合法的,例如:

\\#if( $jazz )

   Vyacheslav Ganelin

\\#end

以上程序的显示结果为:

\ Vyacheslav Ganelin

\

         为了方便理解,注意到 #if(arg)当新一行的结束(return)在输出中会省略新行。因此,#if()块是接着第一个‘\’,显示来自在#if()前面的‘\\’。最后的‘\’在跟文本不同的行,因为那里有新的一行在‘Ganelin’后面,所以最后‘\\’在#end 的前面。

         但是如果$jazzfalse,那么将没有输出。注意:事情也有例外,如果script elements 没有完全的逃避。

\\\#if( $jazz )

    Vyacheslave Ganelin

\\#end

         在这里#if 已经逃避了,但是#end没有,有太多的#end 的产生解析错误。

VTLFormatting issues

尽管在此用户手册中VTL通常都开始一个新行,如下所示:

#set( $imperial = ["Munetaka","Koreyasu","Hisakira","Morikune"] )

#foreach( $shogun in $imperial )

    $shogun

#end

         但是像下面这种写法也是可以的:

Send me #set($foo = ["$10 and ","a cake"])#foreach($a in $foo)$a #end please.

         上面的代码可以被改写为:

Send me

#set( $foo = ["$10 and ","a cake"] )

#foreach( $a in $foo )

$a

#end

please.

或者

Send me

#set($foo       = ["$10 and ","a cake"])

                 #foreach           ($a in $foo )$a

         #end please.

每一种的输出结果将一样。

 

 

其他特性和杂项

math

在模板中可以使用Velocity内建的算术函数,如:加、减、乘、除。下面的等式分别是:加、减、乘、除:

#set( $foo = $bar + 3 )

#set( $foo = $bar – 4 )

#set( $foo = $bar * 6 )

#set( $foo = $bar / 2 )

当执行除法时将返回一个Integer类型的结果。而余数你可以使用%来得到:

#set( $foo = $bar % 5 )

Velocity内使用数学计算公式时,只能使用像-n,-2,-1,0,1,2,n这样的整数,而不能使用其它类型数据。当一个非整型的对象被使用时它将被logged并且将以null作为输出结果。

 

Range Operator

Range operator可以被用于与#set#foreach statement联合使用。对于处理一个整型数组它是很有用的,Range operator具有以下构造形式:

[n..m]

mn都必须是整型,而m是否大于n则无关紧要。例子:

First example:

#foreach( $foo in [1..5] )

$foo

#end

 

Second example:

#foreach( $bar in [2..-2] )

$bar

#end

 

Third example:

#set( $arr = [0..1] )

#foreach( $i in $arr )

$i

#end

 

Fourth example:

[1..3]

产生以下输出:

First example:

1 2 3 4 5

 

Second example:

2 1 0 -1 -2

 

Third example:

0 1

 

Fourth example:

[1..3]

注意:range operator只在#set#foreach中有效。

         网页设计者使用标准尺寸制作表格,但是某时候会没有足够的数据填入表格,就会发现 range 非常有用。

 

Advanced IssueEscaping and

当一个reference分隔时,并且在它之前有逃逸符时,reference将以特殊的方式处理。注意这种方式与标准的逃逸方式时不同的。对照如下:

#set( $foo = "bar" )

$\!foo

$\!{foo}

$\\!foo

$\\\!foo

         这将显示为:

$!foo

$!{foo}

$\!foo

$\\!foo

         \ $ 前面跟逃避规则相比较;

\$foo

\$!foo

\$!{foo}

\\$!{foo}

         这将显示为:

\$foo

\$!foo

\$!{foo}

\bar

 

Velocimacro杂记

         这章节是一个小型涉及Velocimacros常见问题解答。这章节会随时间的变化而变化,所以它的作用有时是检验新的信息。

         注意:在这章节'Velocimacro'简称为‘VM’。

 

1.        我能用一个标识或者另外的VM 作为一个VM的参数吗?

例如:#center ( #bold( “hello” ) )

 

答:不可以。一个directive的参数使用另外一个directive是不合法的。

但是,还是有些事情你可以作的。最简单的方式就是使用双引号,所以你可以这样做:

#set($stuff = "#bold('hello')" )

#center( $stuff )

         上面的格式也可以缩写为一行:

#center( "#bold( 'hello' )" )

         请注意在下面的例子中参数被evaluatedVelocimacro内部,而不是在calling level。例子:

 

#macro( inner $foo )

  inner : $foo

#end

 

#macro( outer $foo )

   #set($bar = "outerlala")

   outer : $foo

#end

 

#set($bar = 'calltimelala')

#outer( "#inner($bar)" )

 

输出结果为:

Outer : inner : outerlala

         因为#inner($bar)的赋值发生在#outer(),所以$bar的值在#outer()赋给的。

记住Veloctiy的特性:参数的传递是By Name的。例如:

#macro( foo $color )

  <tr bgcolor=$color><td>Hi</td></tr>

  <tr bgcolor=$color><td>There</td></tr>

#end

 

#foo( $bar.rowColor() )

         以上代码将导致rowColor()方法两次调用,而不是一次。为了避免这种现象的出现,我们可以按照下面的方式执行:

#set($color = $bar.rowColor())

#foo( $color )

 

2.我能通过#parse()登记velocimacros吗?

 

答:目前,Velocimacros必须在第一次被模板调用前被定义。这就意味着你的#macro()声明应该出现在使用Velocimacros之前。

如果你试图#parse()一个包含#macro() directive的模板,这一点是需要牢记的。因为#parse()发生在运行期,但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素,这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果,只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs

 

3.什么是velocimacro自动加载?

 

答:velocimacro.library.autoreload是专门为开发而非产品使用的一个属性。此属性的默认值是false

<type>.resource.loader.cache = false 连同一起设为true ()(<type>是你用的资源器,例如‘file),当你编译它们的时候Velocity引擎会自动重载那些在你的velocimacro库文件中的修改,所以你不用servlet引擎(或者应用程序)或者其他窍门来使你的velocimacros重载。

         这里有一个简单的属性配置。

   file.resource.loader.path = templates

   file.resource.loader.cache = false

   velocimacro.library.autoreload = true

    不要在你的作品中出现上述的配置

 

 

字符串串联

开发人员最常问的问题是我如何作字符拼接?在java中是使用号来完成的。

VTL里要想实现同样的功能你只需要将需要联合的reference放到一起就行了。例如:

      #set( $size = "Big" )

       #set( $name = "Ben" )

 

      The clock is $size$name.

   输出结果将是:The clock is BigBen.。更有趣的情况是:

      #set( $size = "Big" )

      #set( $name = "Ben" )

 

      #set($clock = "$size$name" )

 

      The clock is $clock.

   

上例也会得到同样的结果。最后一个例子,当你希望混合固定字段到你的reference时,你需要使用标准格式:

      #set( $size = "Big" )

      #set( $name = "Ben" )

 

      #set($clock = "${size}Tall$name" )

 

      The clock is $clock.

   

输出结果是:The clock is BigTallBen.。使用这种格式主要是为了使得$size不被解释为$sizeTall

 

 

反馈

         如果你在手册中遇到任何错误或者有其他关于Velocity用户手册的反馈,请email Velocity  user list。谢谢!

[C#]NVelocity介绍

mikel阅读(1181)

nVelocity是一个基于.NET的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由.NET代码定义的对象。

nVelocity 应用于web开发时,界面设计人员可以和.NET程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只关注页面的显示效果,而由.NET程序开发人员关注业务逻辑编码。nVelocity.NET代码从web页面中分离出来,这样为web站点的长期维护提供了便利,同时也为我们在aspx之外又提供了一种可选的方案。

nVelocity的能力远不止web站点开发这个领域,例如,它可以从模板(template)产生SQLPostScriptXML,它也可以被当作一个独立工具来产生源代码和报告,或者作为其他系统的集成组件使用。nVelocity也可以为很多web开发架构提供模板服务(template service)。我们的系统就提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。

1.2 nVelocity能为我们作什么?

假设你是一家专门出售Mud的在线商店的页面设计人员,让我们暂且称它为“在线MUD商店”。你们的业务很旺,客户下了各种类型和数量的Mud订单。他们都是通过输入用户名和密码后才登陆到你的网站,登陆后就允许他们查看订单并购买更多的Mud。现在,一种非常流行的mud正在打折销售。另外有一些客户规律性的购买另外一种也打折但不是很流行的Bright Red Mud,由于购买的人并不多所以它被安置在页面的边缘。所有用户的信息都是被跟踪并存放于数据库中的,所以某天有一个问题可能会冒出来:为什么不使用nVelocity来使用户更好的浏览他们感兴趣的商品呢? 

nVelocity使得web页面的客户化工作非常容易。作为一个站点的设计人员,你希望每个用户登陆时都拥有自己的页面。 

你会见了一些公司内的软件工程师,你发现他们每个人都同意客户应该拥有具有个性化的信息。那让我们把软件工程师应该作的事情放在一边,看一看你应该作些什么吧。

你可能在页面内嵌套如下的VTLnVelocity template language)声明:

<html>
<body>
Hello $customer.Name!
<table>
#foreach( $mud in $nudsOnSpecial )
#if ( $customer.hasPurchased( $mud ) )
<tr><td>$flogger.getPromo( $mud )</td></tr>
#end
#end
</table>

1.1 VTL (nVelocity template language)

VTL意味着提供最简单、最容易并且最整洁的方式合并页面动态内容。 VTL 使用references来在站点内嵌套动态内容,一个变量就是一种类型的reference。变量是某种类型的reference,它可以指向.NET代码中的定义,或者从当前页面内定义的VTL statement得到值。下面是一个VTL statement的例子,它可以被嵌套到 HTML代码中:

#set ( $a = “Velocity” )

和所有的VTL statement一样,这个statement以#字符开始并且包含一个directiveset。当一个在线用户请求你的页面时,nVelocity Template Engine将查询整个页面以便发现所有#字符,然后确定哪些是VTL statement,哪些不需要VTL 作任何事情。 

#字符后紧跟一个directiveset时,这个set directive使用一个表达式(使用括号封闭)将一个值设置给变量。变量被列在左边,而它的值被列在右边,最后他们之间使用=号分割。

在上面的例子中,变量是$a,而它的值是Velocity。和其他的references一样以$字符开始,而值总是以双引号封闭。Velocity中仅有String可以被赋值给变量。

记住以下的规则: 

使用$字符开始的references用于得到什么;使用#字符开始的directives用于作些什么。

1.2 Hello nVelocity World

一旦某个变量被分配了一个值,那么你就可以在HTML文件的任何地方引用它。在下面的例子中,一个值被分配给$foo变量,并在其后被引用。 

<html> 

<body> 

#set ( $foo = “nVelocity” ) 

Hello $foo World! 

</body> 

</html> 

上面的实现结果是在页面上打印“Hello nVelocity World!”

为了使包含VTL directivesstatement更具有可读性,我们鼓励你在新行开始一个VTL statement,尽管你不是必须这么作。Set directive将在后面详细描述。

1.1 注释

单行注释:

 ## This is a single line comment.

多行注释: 

 #* 

   Thus begins a multi-line comment. Online visitors won’t

   see this text because the Velocity Template Engine will ignore it.

 *# 

文档格式: 

 #** 

   This is a VTL comment block and 

   may be used to store such information as the document author

and versioning information: 

   @version 5 

   @author 

 *# 

1.2 References

VTL中有三种类型的references:变量(variables)、属性(properties)、方法 (methods)。作为一个使用VTL的页面设计者,你和你的工程师必须就references的名称达成共识,以便你可以在你的template中使用它们。

VTL中一切reference可以作为一个String对象处理。如果有一个对象$foo是一个Integer对象,那么Velocity将调用它的toString()方法将这个对象转型为String类型。

1.2.1 变量

格式要求同.NET

1.1.1 属性

例子:

$customer.Address

$purchase.Total

$customer.Address有两种含义。它可以表示:查找hashtable对象customer中以Address为关键字的值;也可以表示调用customer对象的 getAddress()方法。当你的页面被请求时,Velocity将确定以上两种方式选用那种,然后返回适当的值。

1.1.2 方法

一个方法就是被定义在.NET中的一段代码,并且它有完成某些有用工作的能力,例如一个执行计算和判断条件是否成立、满足等。方法是一个由$开始并跟随VTL标识符组成的References,一般还包括一个VTL方法体。例如:

$customer.getAddress()

$purchase.getTotal()

$page.setTitle( “My Home Page” )

$person.setAttributes( [“Strange”, “Weird”, “Excited”] )

前两个例子$customer.getAddress()$purchase.getTotal()看起来挺想上面的属性$customer.Address $purchase.Total。如果你觉得他们之间有某种联系的话,那你是正确的。

VTL属性可以作为VTL方法的缩写。$customer.Address属性和使用$customer.getAddress()方法具有相同的效果。如果可能的话使用属性的方式是比较合理的。属性和方法的不同点在于你能够给一个方法指定一个参数列表。

1.1.1 正式reference标记

reference正式格式如下:

${mudSlinger}变量

${customer.Address}属性

${purchase.getTotal()}方法

正式格式更见常用,但是有时还是使用正是格式比较适合。例如:你希望通过一个变量$vice来动态的组织一个字符串。

Jack is a $vicemaniac.

本来变量是$vice现在却变成了$vicemaniac,这样Veloctiy就不知道您到底要什么了。所以,应该使用正是格式书写

Jack is a ${vice}maniac

现在Velocity知道变量是$vice而不是$vicemaniac

1.1.2 Quiet reference notation

如:

<input type=”text” name=”email” value=”$email” />

当页面的form被初始加载时,变量$email还没有值,这时你肯定是希望它能够显示一个空白来代替输出”$email”这样的字段。那么使用quiet reference notation就比较合适。

<input type=”text” name=”email” value=”$!email”/>

这样文本框的初始值就不会是email而是空值了。

正式和quiet格式的reference notation也可一同使用,像下面这样:

<input type=”text” name=”email” value=”$!{email}”/>

1.1.1 Getting literal

nVelocity使用特殊字符$#来帮助它工作,所以如果要在template里使用这些特殊字符要格外小心。本节将讨论$字符。

VTL中使用$2.5这样的货币标识是没有问题得的,VTL不会将它错认为是一个reference,因为VTL中的reference总是以一个大写或者小写的字母开始。

VTL中使用“"”作为转义字符。

例如:

#set( $email = “foo” )

$email

"$email

""$email

"""$email

将输出为:

foo

$email

"foo

""$email

如果email变量没有被定义则

$email

"$email

""$email

"""$email

将被输出为:

$email

"$email

""$email

"""$email

注意:VTL中未被定义的变量将被认为是一个字符串,所以以下例子:

#set( $foo = “gibbous” )

$moon = $foo

的输出结果是:

$moon = gibbous

现在你已经对reference比较熟悉了,你可以将他们高效的应用于你的template了。nVelocity利用了很多.NET规范以方便了设计人员的使用。例如:

$foo

$foo.getBar()

##与下面这句相同

$foo.Bar

$data.getUser(“john”)

##与下面这句相同

$data.User(“john”)

$data.getRequest().getServerName()

#与下面这句相同

$data.Request.ServerName

##与下面这句相同

${data.Request.ServerName}

但是,注意VTL中不会将reference解释为对象的实例变量。例如:$foo.Name将被解释为Foo对象的getName()方法,而不是Foo对象的Name实例变量。

1.1 Directives

Reference允许设计者使用动态的内容,而directive使得你可以应用.NET代码来控制你的显示逻辑,从而达到你所期望的显示效果。

1.1.1 赋值#set

#set directive被用于设置一个reference的值。例如:

#set ( $primate = “monkey” )

#set ( $customer.Behavior = $primate )

赋值左侧的(LHS)必须是一个变量或者属性reference。右侧(RHS)可以是以下类型中一种:

变量reference

字符串

属性reference

方法reference

数字

ArrayList

下面是应用各种类型的RHS的例子:

set ( $monkey = $bill ) ##变量reference

set ( $monkey.Friend = “monica” ) ##字符串

set ( $monkey.Blame = $whitehouse.Leak ) ##属性reference

set ( $monkey.Plan = $spindoctor.weave($web) ) ##方法reference

set ( $monkey.Number = 123 ) ##数字

set ( $monkey.Say = [“Not”, $my, “fault”] ) ##ArrayList

注意:最后一个例子的取值方法为:$monkey.Say.get(0)

RHS也可以是一个简单的算术表达式:

#set ( $value = $foo + 1 )

#set ( $value = $bar -1 )

#set ( $value = $foo * $bar )

#set ( $value = $foo / $bar )

如果你的RHS是一个nullVTL的处理将比较特殊:它将指向一个已经存在的reference,这对初学者来讲可能是比较费解的。例如:

#set ( $resut = $query.criteria(“name”) )

结果是$result

#set ( $resut = $query.criteria(“address”) )

结果是$result

如果$query.criteria(“name”)返回一个“bill”,而$query.criteria(“address”)返回的是null,则显示的结果如下:

结果是bill

结果是bill

看看下面的例子:

#set( $criteria = ["name", "address"] )

#foreach( $criterion in $criteria )

#set( $result = $query.criteria($criterion) )

#if( $result )

Query was successful

#end

#end

在上面的例子中,程序将不能智能的根据$result的值决定查询是否成功。在$result#set后(added to the context),它不能被设置回nullremoved from the context)。打印的结果将显示两次查询结果都成功了,但是实际上有一个查询是失败的。

为了解决以上问题我们可以通过预先定义的方式:

#set( $criteria = [“name”, “address”] )

#foreach( $criterion in $criteria )

#set( $result = false )

#set( $result = $query.criteria( $criterion ) )

#if( $result )

Query was successful

#end

#end

1.1.1 字符串

当你使用#set directive,字符串封闭在一对双引号内。

#set ( $directoryRoot = “www” )

#set ( $templateName = “index.vm” )

#set ( $template = “$directoryRoot/$tempateName” )

$template

上面这段代码的输出结果为:www/index.vm

但是,当字符串被封装在单引号内时,它将不被解析:

#set ( $foo = “bar” )

$foo

#set ( $blargh = ‘$foo' )

结果:

bar

$foo

上面这个特性可以通过修改Views目录下的nvelocity.properties文件的stringliterals.interpolate = false的值来改变上面的特性是否有效。

1.1.2 条件语句

if/elseif/else

当一个web页面被生成时使用Velocity#if directrive,如果条件成立的话可以在页面内嵌入文字。例如:

#if ( $foo )

<strong>nVelocity!</strong>

#end

上例中的条件语句将在以下两种条件下成立:

$foo是一个boolean型的变量,且它的值为true

$foo变量的值不为null

这里需要注意一点:Velocity context仅仅能够包含对象,所以当我们说“boolean”时实际上代表的时一个Boolean对象。即便某个方法返回的是一个boolean值,Velocity也会利用内省机制将它转换为一个Boolean的相同值。

如果条件成立,那么#if#end之间的内容将被显示。

#elseif#else元素可以同#if一同使用。例如:

#if( $foo < 10 )

<strong> Go North </strong>

#elseif( $foo == 10 )

<strong> Go East </strong>

#elseif( $foo == 6 )

<strong> Go South </strong>

#else

<strong> Go West </strong>

#end

注意这里的nVelocity的数字是作为Integer来比较的――其他类型的对象将使得条件为false,它使用==来比较两个值,而且nVelocity要求等号两边的值类型相同。

[Lucene]《Lucene天书》 Lucene的文件系统

mikel阅读(906)

Lucene的文件系统分为内存和硬盘两个部分,文件逻辑组织方式暂且不提,本文将关注其物理结构,包括它在内存中如何存放,以及如何写入硬盘。

目录
一、相关类
   1.1 Directory
   1.2 IndexInput和IndexOutput
   1.3 RAMFile
二、索引概述
   2.1 IndexOutput
 2.2 RAMOutputStream和RAMFile
   2.3 内存文件是如何写入硬盘的

一、相关类

1.1 Directory
一个 Directory在Lucene系统中可以被称为“一份索引”,可以有许多份索引共同提供搜索数据源,而一个Directory就是对其每份索引的描 述。它可以存放在内存中,也可以存放于硬盘上。存放在内存中的时候,是使用它子类RAMDirectory的实例,存放在硬盘上的时候是使用 FSDirectory的实例。

图 1.1.1 Directory类图

1.2 IndexInput和IndexOutput
IndexInput负责读取,IndexOutput负责写入。其子类负责具体的工作。这里暂且不多讲。


图1.2.1 IndexOutput和IndexInput

1.3 RAMFile
RAMFile是Lucene文件系统内存文件的基本单位。一个Directory可以包含N个RAMFile(N >= 2)。

二、索引概述
这里不讨论索引的逻辑结构,而只单纯地看索引是如何在内存中存储,如何持久化到硬盘。


图 2.1 索引过程

图2.1体现整个过程。首先调用IndexWriter类的AddDocument方法,这 个是外部调用,走入内部,是由IndexWriter调用了DocumentWriter的AddDocument方法。然后 DocumentWriter完成调用自身的UpdateDocument,ProcessDocument,ProcessField方法,再转入 FieldWriter的WriteField方法,这个方法又调用了IndexOutput里WriteInt或者其它一些按类型写入的方法,最终由 RAMOutputStream调用自身的WriteByte写入内存。(IndexOutput里的方法实际存在于RAMOutputStream的实 例。)

2.1 IndexOutput
IndexOutput 主要定义了一些按类型写入的方法,最终都会走向虚方法WriteByte。这些方法在我写的解读Lucene中都有讲到。值得一提的是 WriteChars方法,这个方法看似很复杂,但是其完成的功能却很简单,其实是做了.Net Framework中的Encoding.UTF8.GetBytes方法的工作。看下代码2.1.1和2.1.2的相似之处就可以明白。(代码 2.1.1摘自Lucene IndexOutput源码,2.1.2摘自.Net Framework UTF8Encoding类源码。其自己实现是因为翻译自Java版本。)

代码 2.1.1

 

代码 2.1.2

2.2 RAMOutputStream和RAMFile
RAMOutputStream是IndexOutput的一个子类,其主要点在于三个元素。
1、BUFFER_SIZE 是其缓冲片段的长度;
2、RAMFile 内存文件实例;
3、它的一些方法。
它的方法最终都是完成把数据写入缓冲片段,再把缓冲片段存入RAMFile的实例。它们之间就是这个关系。
RAMOutputStream 需要写入数据,最终会写入一个byte数组,这个数组的长度就是BUFFER_SIZE,由RAMOutputStream调用RAMFile的 AddBuffer方法负责创建这个数组,同时把这个数组作为一个节点存入RAMFile的ArrayList。也就是说RAMFile代表的一个内存文 件,实际上是一个ArrayList数组。而ArrayList数组的每个索引又都由一个byte数组组成,这个byte数组的长度是 BUFFER_SIZE。RAMOutputStream的WriteByte方法负责把数据写入byte数组。(看到这个是不是觉得Lucene的文件 系统根本就不是什么天书嘛,也是很好理解的嘛,呵呵。)

2.3 内存文件如何写入硬盘的
Lucene 文件系统在硬盘的表现实例就是FSDirectory。这个类会负责创建其文件系统所需的描述性文件和数据文件。而具体写入硬盘的工作是由它的内嵌类 FSIndexOutput来完成的。这也就是我们的图1.2.1没有描绘到这个类的原因,这个类继承自BufferedIndexOutput。 BufferedIndexOutput大体上与RAMOutputStream差不多,区别在于其Flush方法与会调用到FSIndexOutput 的FlushBuffer方法,最终把RAMFile的数据写入硬盘文件。

时间原因,这里就不详细介绍了,感兴趣的朋友可以自己去实验一下。

______________________________________________________________________________________________
2009-2-18 by yurow
@ http://birdshover.cnblogs.com

[C#]How to create a simple SWF decompiler

mikel阅读(1378)

Swf Decompiler sample with C#

Goal of this article

This article's main goal is to show how to manage SWF encoded files with .NET, C#, or VB.NET. SWF is a binary format which represents compiled Flash animations.

Using the SwfDotNet library

The SwfDotNet library is a C# open source framework available at:

This library offers some good stuff to read and write SWF format files from version 1 to 7 for now. SwfDotNet.IO.dll is compatible with the .NET framework 1.1, 2.0 and +. This library respects MacromediaTM SWF format specifications. You can read and download the Macromedia official specifications here.

For our decompiler application, we used this framework like in these examples:

Include the SwfDotnNet library in your class:

Collapse
using SwfDotNet.IO;

Create a SwfReader object to get a Swf object:

Collapse
// Create a swf stream reader

SwfReader swfReader = new SwfReader("myfile.swf");
// Read the completed swf file

Swf swf = swfReader.ReadSwf();
// Read only headers of file to optimize read speed

//Swf swf = swfReader.ReadSwfHeader();

// Write swf file version

Console.WriteLine("Version: " + swf.Version);
// Write swf animation dimensions

Console.WriteLine("Size: " + swf.Header.Width +
"x" + swf.Header.Height);
// Write swf fps

Console.WriteLine("Fps: " + swf.Header.Fps);
// Write swf file size

Console.WriteLine("File size: " + swf.Header.FileSize);
// Write swf file frames count

Console.WriteLine("Frames count: " + swf.Header.Frames);
// Write swf file signature (compressed or not)

Console.WriteLine("Signature: " + swf.Header.Signature);
// Closing stream reader

swfReader.Close();

Write in a Swf object and save as an SWF file or stream:

Collapse
// Create a new swf animation

Swf swf = new Swf();
// Define background as blue

swf.Tags.Add(new SetBackgroundColorTag(255, 0, 0));
// Define a new frame

swf.Tags.Add(new ShowFrameTag());
// Define a swf writer

SwfWriter writer = new SwfWriter("myfile.swf");
// Write animation

writer.Write(swf);
// Close the stream

writer.Close();

List of available tag objects in the framework:

Collapse
// Include tags namespace

using SwfDotNet.IO.Tags;
...
// Tags are organized as followed:

// 1. To control the frames flow

// Create a new frame

ShowFrameTag resTag = new ShowFrameTag();
// End of frames sequence

EndTag resTag = new EndTag();
// Define a frame name

FrameLabelTag resTag = new FrameLabelTag();
// Place an object to the screen

PlaceObjectTag resTag = new PlaceObjectTag();
// Place an object to the screen

PlaceObject2Tag resTag = new PlaceObject2Tag();
// Remove an object of the screen

RemoveObjectTag resTag = new RemoveObjectTag();
// Remove an object of the screen

RemoveObject2Tag resTag = new RemoveObject2Tag();
// Define background color

SetBackgroundColorTag resTag = new SetBackgroundColorTag();
// Protect the animation

ProtectTag resTag = new ProtectTag();
// Define tag index of object

SetTabIndexTag resTag = new SetTabIndexTag();
// 2. To embed a picture in an animation

//Add bitmap or gif

DefineBitsTag resTag = new DefineBitsTag();
//Define bitmap

DefineBitsLossLessTag resTag = new DefineBitsLossLessTag();
//Define bitmap with transparency

DefineBitsLossLess2Tag resTag = new DefineBitsLossLess2Tag();
//Define a jpeg compression table

JpegTableTag resTag = new JpegTableTag();
// To include jpeg files

DefineBitsJpeg2Tag resTag = new DefineBitsJpeg2Tag();
//To include transparent jpeg files

DefineBitsJpeg3Tag resTag = new DefineBitsJpeg3Tag();
// 3. To define new button

// To create a simple button

DefineButtonTag resTag = new DefineButtonTag();
// To create a simple button

DefineButton2Tag resTag = new DefineButton2Tag();
// To create a simple button

DefineButtonCxFormTag resTag = new DefineButtonCxFormTag();
// To create a button with sound

DefineButtonSoundTag resTag = new DefineButtonSoundTag();
// 4. To create a new movie clip (container of tags sequence)

DefineSpriteTag resTag = new DefineSpriteTag();
// 5. To add text

// To create an edit text area

DefineEditTextTag resTag = new DefineEditTextTag();
// To use a font

DefineFontTag resTag = new DefineFontTag();
DefineFont2Tag resTag = new DefineFont2Tag();
DefineFontInfoTag resTag = new DefineFontInfoTag();
DefineFontInfo2Tag resTag = new DefineFontInfo2Tag();
// To add static text

DefineTextTag resTag = new DefineTextTag();
// To add static text

DefineText2Tag resTag = new DefineText2Tag();
// 6. Define shapes or morph shapes to draw

// To create morph shapes

DefineMorphShapeTag resTag = new DefineMorphShapeTag();
// To create glyph shapes

DefineShapeTag resTag = new DefineShapeTag();
// To create glyph shapes

DefineShape2Tag resTag = new DefineShape2Tag();
// To create glyph shapes

DefineShape3Tag resTag = new DefineShape3Tag();
// 7. Audio and video embedding

DefineSoundTag resTag = new DefineSoundTag();
SoundStreamBlockTag resTag = new SoundStreamBlockTag();
SoundStreamHeadTag resTag = new SoundStreamHeadTag();
SoundStreamHead2Tag resTag = new SoundStreamHead2Tag();
StartSoundTag resTag = new StartSoundTag();
DefineVideoStreamTag resTag = new DefineVideoStreamTag();
VideoFrameTag resTag = new VideoFrameTag();
// 8. Add action script and debugg

// Add action script code

DoActionTag resTag = new DoActionTag();
// Enable debugger

EnableDebuggerTag resTag = new EnableDebuggerTag();
// Enable debugger

EnableDebugger2Tag resTag = new EnableDebugger2Tag();
// Export assets

ExportAssetsTag resTag = new ExportAssetsTag();
// Import assets

ImportAssetsTag resTag = new ImportAssetsTag();
// Init action

InitActionTag resTag = new InitActionTag();
ScriptLimitTag resTag = new ScriptLimitTag();

Use the byte code decompiler to get the action script code of an Action tag:

Collapse
//include bytecode management namespace

using SwfDotNet.IO.ByteCode;
...
//Browse swf tags list

IEnumerator tagsEnu = swf.Tags.GetEnumerator();
while (tagsEnu.MoveNext())
{
BaseTag tag = (BaseTag)tagsEnu.Current;
if (tag.ActionRecCount != 0)
// The tag contains action script ?

{
IEnumerator byteCodeEnu = tag.GetEnumerator();
while (enum2.MoveNext())
{
//Init the action script decompiler

Decompiler dc = new Decompiler(swf.Version);
//Decompile the current actionscript action from a tag

ArrayList actions = dc.Decompile((byte[])tagsEnu.Current);
foreach (BaseAction obj in actions)
{
//Write action script byte code to the console

Console.WriteLine(obj.ToString());
}
}
}
}

We can directly extract media files from tags:

Collapse
//Browse swf tags list

IEnumerator tagsEnu = swf.Tags.GetEnumerator();
while (tagsEnu.MoveNext())
{
BaseTag tag = (BaseTag)tagsEnu.Current;
if (tag is DefineBitsJpeg2Tag) //Extract a jpeg:

{
//Extract to a file:

((DefineBitsJpeg2Tag)tag).DecompileToFile("extract_image.jpeg");
//Or in a stream:

Image img = ((DefineBitsJpeg2Tag)tag).DecompileToImage();
}
else if (tag is DefineSoundTag) //Extract a sound file:

{
DefineSoundTag soundTag = (DefineSoundTag)tag;
//Extract to a file

if (soundTag.SoundFormat == SoundCodec.MP3)
soundTag.DecompileToFile("extract_sound.mp3");
else
soundTag.DecompileToFile("extract_sound.wav");
}
}

Creating the SWF decompiler sample

The goal of this sample is to create a very simple SWF decompiler. From a compiled FlashTM animation (SWF file), we want to load it, and extract in separate files, all the pictures and all the embedded MP3 files.

Attention: This sample decompiler is not a full SWF decompiler! This sample only decompiles embedded JPEG files without the JPEG table, and only non-streamed MP3 and WAV sounds. Only the action script byte code is extracted too.

You can see a file called SwfDecompiler.exe.log.xml in the project, this file is the log4net framework configuration file. For more information about log4net, you can read this article or go to the official web site.

The SwfDotNet library uses log4net for all the trace operations. When you read an SWF file, you can trace all the SWF tags read by the SwfReader object, and you can trace the different shapes contained by a DefineShape tag for example.

To do it, just change these lines in the SwfDecompiler.exe.log.xml file:

Collapse
<!-- To trace tags readed -->
<logger name="SwfDotNet.IO">
<level value="ALL" />  <!-- Turn it to ALL -->
</logger>
<!-- To trace shapes readed -->
<logger name="SwfDotNet.IO.Tags.Types">
<level value="ALL" />  <!-- Turn it to ALL -->
</logger>

If you change these values and execute the decompiler one more time, a file named "Log.txt" will appear in the same directory of the application, with some lines like this::

Collapse
[DEBUG] - **************** Start to decompile file ..\input\sample.swf
[INFO ] - SetBackgroundColor....OK (5)
[INFO ] - SoundStreamHead2....OK (6)
[INFO ] - DefineBitsJpeg2....OK (43816)      <== A DefineBitsJpeg2
tag has been readed
which contains
43816 octects of data
[INFO ] - Shape: StyleChangeRecord
[INFO ] - Shape: StraightedEdgeRecord
[INFO ] - Shape: StraightedEdgeRecord        <== A straighted edge
shape has been readed
[INFO ] - Shape: StraightedEdgeRecord
[INFO ] - Shape: StraightedEdgeRecord
[INFO ] - Shape: EndShapeRecord
[INFO ] - DefineShape....OK (61)
[INFO ] - PlaceObject2....OK (8)
[INFO ] - DefineSound....OK (8439)
[INFO ] - StartSound....OK (9)
[INFO ] - DefineSound....OK (293373)
[INFO ] - StartSound....OK (9)
[INFO ] - ShowFrame....OK (2)
[INFO ] - ShowFrame....OK (2)
.....

Conclusion

As you can see, thanks to the SwfDotNet library, you can easily analyse, manage, create, read, or write SWF binary files. This sample is a really "light" SWF decompiler but shows the way to do it, shows how to do a real one, and doesn't require to be a binary reading process expert.

Moreover, the SwfDotNet library is 100% free (GPL license) and not platform dependent: you can use it in a web project or in a WinForms application, and, for sure, with Mono.

History

  • 19/12/2006: First article version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Olivier Carpentier

Olivier Carpentier is a french C#.Net Architect, Microsoft Certified Professional.
Software Expert at SQLi, french consulting company
http://www.sqli.com

Occupation: Web Developer
Location: France France

[MVC]Asp.Net Mvc: Model Binding to Simple Types, C

mikel阅读(860)

环境:

Windows 2008, VS 2008 SP1, ASP.NET Mvc RC1

——————————————————————————

本文主要实验如何应用ASP.NET Mvc内建功能(DefaultModelBinder)实现简单类型、复杂类型、集合类型,以及字典类型的自动绑定。

1. 简单类型

这里,我们将下面这个Book类称为简单类型:

public class Book
    {
        
public int BookId { getset; }
        
public string BookName { getset; }
        
public string Author { getset; }
        
public DateTime PublishedDate { getset; }
    }

假设现在需要实现添加Book的功能,那么在BookController中,会定义如下的Action:

[AcceptVerbs(HttpVerbs.Post)]
        
public ActionResult Create(Book book) {
            
//TO DO
            
//Insert book into Database
            return RedirectToAction("Index");
        }

现在的问题便是,在View中如何命名TextBox来达到自动绑定,如下:

<div>
        
<%using (Html.BeginForm("Create""Book")) { %>
        
<div>
            Book Name: 
<%=Html.TextBox("BookName")%>
        
</div>
        
<div>
            Author: 
<%=Html.TextBox("Author")%>
        
</div>
        
<div>
            Published Date: 
<%=Html.TextBox("PublishedDate")%>
        
</div>
        
<div>
            
<input type="submit" id="submit" name="submit" value="submit" />
        
</div>
        
<%%>
    
</div>

注意TextBox的name必须是对应绑定类型的PropertyName(不区分大小写)。 这样,页面表单submit后,我们便可 以在BookController的“Create” Action中得到自动绑定的book对象。这里,ASP.NET MVC还支持在TextBox的name中加上变量名称前缀的情形:

<div>
        
<%using (Html.BeginForm("Create""Book")) { %>
        
<div>
            Book Name: 
<%=Html.TextBox("book.BookName")%>
        
</div>
        
<div>
            Author: 
<%=Html.TextBox("book.Author")%>
        
</div>
        
<div>
            Published Date: 
<%=Html.TextBox("book.PublishedDate")%>
        
</div>
        
<div>
            
<input type="submit" id="submit" name="submit" value="submit" />
        
</div>
        
<%%>
    
</div>

需要注意的是:
1)前缀"book"必须与Action中接收的Book参数名一致
2)如果加了前缀,那么所有的都要加

2. 复杂类型

现在对Book类作些许改动,并引入Author类:

public class Book
    {
        
public int BookId { getset; }
        
public string BookName { getset; }
        
public Author Author { getset; }
        
public DateTime PublishedDate { getset; }
    }
    
public class Author {
        
public int AuthorId { getset; }
        
public string AuthorName { getset; }
        
public string Nation { getset; }
    }

这里,将改动后的Book类称为复杂类。这时,Book类多了一个对Author类的引用。现在,保持BookController中的"Create" Action不变,来看View中的TextBox改如何命名以实现Book类型的自动绑定:

<div>
        
<%using (Html.BeginForm("Create""Book")) { %>
        
<div>
            Book Name: 
<%=Html.TextBox("BookName")%>
        
</div>
        
<div>
            Published Date: 
<%=Html.TextBox("PublishedDate")%>
        
</div>
        
<div>
            Author
's Name: <%=Html.TextBox("Author.AuthorName")%>
        </div>
        
<div>
            Author
's Nation: <%=Html.TextBox("Author.Nation")%>
        </div>
        
<div>
            
<input type="submit" id="submit" name="submit" value="submit" />
        
</div>
        
<%%>
    
</div>

OK,测试通过,想必你也知道命名规则了,要绑定Book类型中的Author类型,必须加上"Author."的前缀。
如果你喜欢,你还可以在所有TextBox名称前面再加"book."的前缀。

3. 集合类型

为避免问题复杂化,我们用回原来的简单Book类型:

public class Book
    {
        
public int BookId { getset; }
        
public string BookName { getset; }
        
public string Author { getset; }
        
public DateTime PublishedDate { getset; }
    }

现在,把BookController的"Create" Action改为接收IList<Book>的参数:

[AcceptVerbs(HttpVerbs.Post)]
        
public ActionResult Create(IList<Book> books) {
            
//TO DO
            
//Insert book into Database
            return RedirectToAction("Index");
        }

然后,在View中运用以下命名规则,以自动绑定IList<book>类型,

<div>
        
<%using (Html.BeginForm("Create""Book")) { %>
        
<div>
            Book Name: 
<%=Html.TextBox("books[0].BookName")%>
        
</div>
        
<div>
            Published Date: 
<%=Html.TextBox("books[0].PublishedDate")%>
        
</div>
        
<div>
            Author
's Name: <%=Html.TextBox("books[0].Author")%>
        </div>
        
<div>
            Book Name: 
<%=Html.TextBox("books[1].BookName")%>
        
</div>
        
<div>
            Published Date: 
<%=Html.TextBox("books[1].PublishedDate")%>
        
</div>
        
<div>
            Author
's Name: <%=Html.TextBox("books[1].Author")%>
        </div>
        
<div>
            
<input type="submit" id="submit" name="submit" value="submit" />
        
</div>
        
<%%>
    
</div>

可以看到如下规则:Action的变量名"books"加上中括号和索引作为前缀,索引必须从0开始,并且必须连续。
通过此命名规则,可以绑定任意集合类型:IList<Book>, ICollection<Book>, IEnumerable<Book>, List<Book>, Book[]等。

4. 字典类型

仍以简单Book类型为例,现在将"Create" Action改为接收IDictionary<Book>类型,

[AcceptVerbs(HttpVerbs.Post)]
        
public ActionResult Create(IDictionary<string, Book> books) {
            
//TO DO
            
//Insert book into Database
            return RedirectToAction("Index");
        }

相应的,View中的命名如下:

<div>
        
<%using (Html.BeginForm("Create""Book")) { %>
        
<div>
            Book SN: 
<%=Html.TextBox("books[0].Key"%>
        
</div>
        
<div>
            Book Name: 
<%=Html.TextBox("books[0].Value.BookName")%>
        
</div>
        
<div>
            Published Date: 
<%=Html.TextBox("books[0].Value.PublishedDate")%>
        
</div>
        
<div>
            Author
's Name: <%=Html.TextBox("books[0].Value.Author")%>
        </div>
        
<div>
            Book SN: 
<%=Html.TextBox("books[1].Key"%>
        
</div>
        
<div>
            Book Name: 
<%=Html.TextBox("books[1].Value.BookName")%>
        
</div>
        
<div>
            Published Date: 
<%=Html.TextBox("books[1].Value.PublishedDate")%>
        
</div>
        
<div>
            Author
's Name: <%=Html.TextBox("books[1].Value.Author")%>
        </div>
        
<div>
            
<input type="submit" id="submit" name="submit" value="submit" />
        
</div>
        
<%%>
    
</div>

可以看出,对于IDictioinary<Book>类型,在命名规则上,相比于集合类型,多了"Key"和"Value”的字眼。另,此规则也适用于Action参数类型为Dictionary<Book>的情形。

总结:

由此看来,只要我们遵循一定的命名规则,就可以轻松实现各种类型的自动绑定了。
当然,ASP.NET MVC还支持自定义ModelBinder扩展,请看下回分解。:-)

参考文献:
Scott Hanselman:ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries

Copyright

作者: Tristan G
本文版权归作者和博客园共有,欢迎转载 🙂

[C#].NET深入学习笔记(3):垃圾回收与内存管理

mikel阅读(919)

1..Net的类型和内存分配

     Net中的所有类型都是(直接或间接)从System.Object类型派生的。

    CTS中的类型被分成两大类——引用类型(reference type,又叫托管类型[managed type]),分配在内存堆上,值类型(value type)。值类型分配在堆栈上。如图

   

     值类型在栈里,先进后出,值类型变量的生命有先后顺序,这个确保了值类型变量在推出作用域以前会释放资源。比引用类型更简单和高效。堆栈是从高地址往低地址分配内存。

     引用类型分配在托管堆(Managed Heap)上,声明一个变量在栈上保存,当使用new创建对象时,会把对象的地址存储在这个变量里。托管堆相反,从低地址往高地址分配内存,如图

  

    2.GC垃圾收集器的工作原理

      上图中,当dataSet使用过期以后,我们不显示销毁对象,堆上的对象还继续存在,等待GC的 回收。

      在new对象时,要先搜索空闲链表,找到最适合内存块,分配,调整内存块链表,合并碎片。new操作几乎可以在O(1)的时间完成,把 堆顶指针加1。工作原理是: 当托管堆上剩余空间不足,或者Generator 0 的空间已满的时候GC运行,开始回收内存。垃圾回收的开始,GC对堆内存的压缩调整,对象集中到顶部。GC在扫描垃圾的时候会占用一定的CPU时间片的, 最初的GC算法真的是扫描整个堆,效率低。现在的GC把堆中的对象分成3代,最早进入堆的是第0代(generation 0), 其次是generation 1, generation2. 第一次GC只扫描第0代。如果回收的空间足够当前使用就不必扫描其它generation的对象。所以,GC创建对象的效率比C++高效,不需要扫描全部 堆空间。它通过扫描策略,再加上内存管理策略带来的性能提升,足以补偿GC所占用的CPU时间。

    3.什么是非托管资源

  常见 的非托管资源就是包装操作系统资源的对象,例如文件,窗口或网络连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它知道如何清 理这些资源。好在.net Framework提供的Finalize()方法,它允许在垃圾回收器回收该类资源前,适当的清理非托管资源。这里列举几种常见的非托管资源:画笔、流 对象、组件对象等等资源 (Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,ApplicationContext,Brush,

Component,ComponentDesigner,Container,Context,Cursor,FileStream,

Font,Icon,Image,Matrix,Timer,Tooltip)。(参考MSDN)

    4.如何有效释放非托管资源。

     GC无法管理非托管资源,那么如何释放非托管资源呢?.Net提供了两种方式:

(1)析构函数:垃圾收集器回收非托管对象的资源时,会调用对象的终结方法Finalize(),进行资源的清理工作,但是由于GC工作规则的限制,GC调用对象的Finalize方法,第一次不会释放资源,第二次调用之后才删除对象。

(2)继承IDisposable接口,实现Dispose()方法,IDisposable接口定义了一个模式(具有语言级的支持),为释放未托管的资源提供了确定的机制,并避免产生析构函数固有的与垃圾收集器相关的问题。

   为了更好的理解垃圾回收机制,我特地写了部分代码,里面添加了详细的注释。定义单个类FrankClassWithDispose(继承接口IDisposableFrankClassNoFinalize(没终结器)FrankClassWithDestructor(定义了析构函数)。

具体代码如下:


 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Data;
 5using System.Data.Odbc;
 6using System.Drawing;
 7//Coded By Frank Xu Lei 18/2/2009
 8//Study the .NET Memory Management
 9//Garbage Collector 垃圾收集器。可以根据策略在需要的时候回收托管资源,
10//但是GC不知道如何管理非托管资源。如网络连接、数据库连接、画笔、组件等
11//两个机制来解决非托管资源的释放问题。析构函数、IDispose接口
12//COM引用计数
13//C++手动管理,New Delete
14//VB自动管理
15namespace MemoryManagement
16{
17    //继承接口IDisposable,实现Dispose方法,可以释放FrankClassDispose的实例资源
18    public class FrankClassWithDispose : IDisposable
19    {
20        private OdbcConnection _odbcConnection = null;
21        
22        //构造函数
23        public FrankClassWithDispose()
24        {
25            if (_odbcConnection == null)
26                _odbcConnection = new OdbcConnection();
27            Console.WriteLine("FrankClassWithDispose has been created ");
28        }

29        //测试方法
30        public void DoSomething()
31        {
32
33            ////code here to do something
34            return ;
35        }

36        //实现Dispose,释放本类使用的资源
37        public void Dispose()
38        {
39            if (_odbcConnection != null)
40                _odbcConnection.Dispose();
41            Console.WriteLine("FrankClassWithDispose has been disposed");
42        }

43    }

44    //没有实现Finalize,等着GC回收FrankClassFinalize的实例资源,GC运行时候直接回收
45    public class FrankClassNoFinalize
46    {
47        private OdbcConnection _odbcConnection = null;
48        //构造函数
49        public FrankClassNoFinalize()
50        {
51            if (_odbcConnection == null)
52                _odbcConnection = new OdbcConnection();
53            Console.WriteLine("FrankClassNoFinalize  has been created");
54        }

55        //测试方法
56        public void DoSomething()
57        {
58
59            //GC.Collect();
60            ////code here to do something
61            return ;
62        }

63    }

64    //实现析构函数,编译为Finalize方法,调用对象的析构函数
65    //GC运行时,两次调用,第一次没释放资源,第二次才释放
66    //FrankClassDestructor的实例资源
67    //CLR使用独立的线程来执行对象的Finalize方法,频繁调用会使性能下降
68    public class FrankClassWithDestructor
69    {
70        private OdbcConnection _odbcConnection = null;
71        //构造函数
72        public FrankClassWithDestructor()
73        {
74            if (_odbcConnection == null)
75                _odbcConnection = new OdbcConnection();
76            Console.WriteLine("FrankClassWithDestructor  has been created");
77        }

78        //测试方法
79        public void DoSomething()
80        {
81            ////code here to do something
82
83            return ;
84        }

85        //析构函数,释放未托管资源
86        ~FrankClassWithDestructor()
87        {
88            if (_odbcConnection != null)
89                _odbcConnection.Dispose();
90            Console.WriteLine("FrankClassWithDestructor  has been disposed");
91        }

92    }

93}

94

其中使用了非托管的对象OdbcConnection的实例。建立的客户端进行了简单的测试。客户端代码如下:

 

 


 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Data;
 5using MemoryManagement;
 6//Coded By Frank Xu Lei 18/2/2009
 7//Study the .NET Memory Management
 8//Test The Unmanaged Objects Reclaimed.
 9//针对非托管代码的测试,比较
10//托管代码,GC可以更具策略自己回收,也可以实现IDisposable,调用Dispose()方法,主动释放。
11namespace MemoryManagementClient
12{
13    class Program
14    {
15        static void Main(string[] args)
16        {
17
18            /////////////////////////////////////////(1)////////////////////////////////////////////
19            //调用Dispose()方法,主动释放。资源,灵活
20            FrankClassWithDispose _frankClassWithDispose = null;
21            try
22            {
23                _frankClassWithDispose = new FrankClassWithDispose();
24                _frankClassWithDispose.DoSomething();
25                
26            }

27            finally
28            {
29                if (_frankClassWithDispose!=null)
30                _frankClassWithDispose.Dispose();
31                //Console.WriteLine("FrankClassWithDispose实例已经被释放");
32            }

33                
34            /////////////////////////////////////////(2)//////////////////////////////////////////////
35            //可以使用Using语句创建非托管对象,方法执行结束前,会调用
36            using (FrankClassWithDispose _frankClassWithDispose2 = new FrankClassWithDispose())
37            {
38                //_frankClassWithDispose2.DoSomething();
39            }

40
41            /////////////////////////////////////////(3)////////////////////////////////////////////
42            //垃圾收集器运行的时候,一次就释放资源
43            FrankClassNoFinalize _frankClassNoFinalize = new FrankClassNoFinalize();
44            _frankClassNoFinalize.DoSomething();
45             
46            //////////////////////////////////////////(4)//////////////////////////////////////////////
47            //垃圾收集器运行的时候,两次才能够释放资源
48            FrankClassWithDestructor _frankClassWithDestructor = new FrankClassWithDestructor();
49            _frankClassWithDestructor.DoSomething();
50            ///////////////////////////////////////////(5)/////////////////////////////////////////////
51            //不能使用Using语句来创建对象,因为其没实现IDispose接口
52            //using (FrankClassWithDestructor _frankClassWithDestructor2 = new FrankClassWithDestructor())
53            //{
54            //    _frankClassWithDestructor2.DoSomething();
55            //}
56
57            //////////////////////////////////////////////////////////////////////////////////////
58            //For Debug
59            Console.WriteLine("Press any key to continue");
60            Console.ReadLine();
61
62        
63        }

64    }

65}

66

 

值得一提的是:调用Dispose()方法,主动释放资源,灵活,可以使用Using语句创建非托管对象,方法执行结束前,会调用
Dispose()方法释放资源,
这两端代码的效果是一样的,可以查看编译后IL。

 

 

Code

Using 语句有同样的效果,来实现非托管对象资源的释放。这点在面试中也会经常遇到,Using关键字的用法有哪几种等等类似的问题。基本理想的答案都是除了引用 命名空间,和命名空间设置别名外,就是这个用法实现如try finally块一样作用的对非托管对象资源的回收。只是一种简便的写法。

    总结:看了本文以后,不知对你是否有所帮助,如果你理解了.net垃圾回收的机制和GC的工作原理,以及包含如何管理非托管资源,你就会成为一个内存管理 的高手。如果面试官问道这个问题,你就可以详细阐述你对这类问题的理解和看法。希望这篇文章能对你的工作和学习带来帮助~

DemoCodes/Files/frank_xl/MemoryManagement2008.zip

【作者】Frank Xu Lei

【地址】 http://www.cnblogs.com/frank_xl/archive/2009/02/19/1393566.html

[C#].net中上传视频并将各种视频文件转换成.flv格式

mikel阅读(969)

首先,我们部署一下文件夹.在工程的目录下新建几个文件夹如下图:

UpFiles文件夹是要保存你上传的文件,PlayFiles文件夹是用于你转换后保存的文件(用于网上播放)
ImgFile文件夹是保存截取视频文件的图片,然后那两个mencoder和ffmpeg文件夹是视频转换工具.此视频转换也
可叫做mencoder+ffmpeg视频转换.
首先,在配置文件中给这些文件夹进行路径的配置.如下

CODE:
<appSettings>
<!–工具文件夹–>
<add key="ffmpeg" value="ffmpeg/ffmpeg.exe"/>
<add key="mencoder" value="mencoder/mencoder.exe"/>
<add key="mplayer" value="mencoder/mplayer.exe"/>
<!–上传文件的路径–>
<add key="upfile" value="UpFiles"/>
<!–上专文件图片路径–>
<add key="imgfile" value="ImgFile"/>
<!–上传文件图片大小–>
<add key="CatchFlvImgSize" value="240×180"/>
<add key="widthSize" value="400"/>
<add key="heightSize" value="350"/>
<!–转换后文件路径–>
<add key="playfile" value="PlayFiles"/>
</appSettings>

在上传.ASPX页面中

CODE:
标题:<asp:TextBox ID="txtTitle" runat="server" Width="358px"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ControlToValidate="txtTitle"
ErrorMessage="标题不为空"></asp:RequiredFieldValidator>
<br />
<asp:FileUpload ID="FileUpload1" runat="server" Width="339px" />
<asp:Button ID="btnUpload" runat="server" OnClick="btnUpload_Click" Text="上传视频" Width="70px" />
文件类型<span style="color:Red;">(.asf|.flv|.avi|.mpg|.3gp|.mov|.wmv|.rm|.rmvb)</span>
<asp:RegularExpressionValidator ID="imagePathValidator" runat="server" ErrorMessage="文件类型不正确"
ValidationGroup="vgValidation" Display="Dynamic" ValidationExpression="^[a-zA-Z]:(\\.+)(.asf|.flv|.avi|.mpg|.3gp|.mov|.wmv|.rm|.rmvb)$"
ControlToValidate="FileUpload1">
</asp:RegularExpressionValidator>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="FileUpload1"
ErrorMessage="文件不为空"></asp:RequiredFieldValidator></div>
<div style=" height:0px; border-top:solid 1px red; font-size:0px;"></div>
<div>上传列表.</div>

程序工作的原理是:视频先上传上去,然后跟着就是开始转换.
所以.在CS 文件中有
这里为了方便我是用SQLDatasource数据控件做数据的操作.

CODE:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class _Default : System.Web.UI.Page
{
// 扩展名定义
string[] strArrFfmpeg = new string[] {"asf","avi","mpg","3gp","mov" };
string[] strArrMencoder = new string[] {"wmv","rm","rmvb" };
protected void Page_Load(object sender, EventArgs e)
{
}
//
protected void btnUpload_Click(object sender, EventArgs e)
{
string upFileName = "";
if (this.FileUpload1.HasFile)
{
string fileName = PublicMethod.GetFileName(this.FileUpload1.FileName);// GetFileName();
if ((string)Session["file"] == fileName)
{
return;
}
upFileName = Server.MapPath(PublicMethod.upFile + fileName);
this.FileUpload1.SaveAs(upFileName);
string saveName = DateTime.Now.ToString("yyyyMMddHHmmssffff") ;
string playFile = Server.MapPath(PublicMethod.playFile + saveName);
string imgFile = Server.MapPath(PublicMethod.imgFile + saveName);
//System.IO.File.Copy(Server.MapPath(PublicMethod.playFile + "00000002.jpg"), Server.MapPath(PublicMethod.imgFile+"aa.jpg"));
PublicMethod pm = new PublicMethod();
string m_strExtension = PublicMethod.GetExtension(this.FileUpload1.PostedFile.FileName).ToLower();
if (m_strExtension == "flv")
{//直接拷贝到播放文件夹下
System.IO.File.Copy(upFileName, playFile+".flv");
pm.CatchImg(upFileName, imgFile);
}
string Extension = CheckExtension(m_strExtension);
if (Extension == "ffmpeg")
{
  pm.ChangeFilePhy(upFileName, playFile, imgFile);
}
else if (Extension == "mencoder")
{
pm.MChangeFilePhy(upFileName, playFile, imgFile);
}
InsertData(this.txtTitle.Text, fileName,saveName);
Session["file"] = fileName;
}
}
//
private string CheckExtension(string extension)
{
string m_strReturn = "";
foreach (string var in this.strArrFfmpeg)
{
if (var == extension)
{
m_strReturn = "ffmpeg"; break;
}
}
if (m_strReturn == "")
{
foreach (string var in strArrMencoder)
{
if (var == extension)
{
m_strReturn = "mencoder"; break;
}
}
}
return m_strReturn;
}
#region 插入数据到数据库中
private void InsertData(string MediaName,string fileName,string saveName)
{
//string name=fileName.Substring(0, fileName.LastIndexOf('.'));
string imgName = saveName + ".jpg";//图片文件名;
string playName = saveName + ".flv";
string SQLstr = "insert into Media(FMediaName,FMediaUpPath,FMediaPlayPath,FMediaImgPath) values(@MName,@MUppath,@MPlaypath,@MImgpath)";
//string constr = ConfigurationManager.ConnectionStrings["sqlcon"].ToString();
SqlDataSource1.InsertCommand = sqlstr;
SqlDataSource1.InsertCommandType = SqlDataSourceCommandType.Text;// CommandType.Text;
SqlDataSource1.InsertParameters.Add("MName",MediaName);
SqlDataSource1.InsertParameters.Add("MUppath",PublicMethod.upFile+fileName);
SqlDataSource1.InsertParameters.Add("MPlaypath",PublicMethod.playFile+playName);
SqlDataSource1.InsertParameters.Add("MImgpath",PublicMethod.imgFile+imgName);
SqlDataSource1.Insert();
}
#endregion
}
PublicMethod类如下:
在这个类里面主要是做文件转换和保存,在转换文件的时候CPU的利用率可以达100%.
它的主要原理是另起一个进程在转换的时候你会发现你的进程里多了一个.
using System;
using System.Configuration;
/// <summary>
/// Summary description for PublicMethod
/// </summary>
public class PublicMethod:System.Web.UI.Page
{
public PublicMethod()
{
}
//文件路径
public static string ffmpegtool = ConfigurationManager.AppSettings["ffmpeg"];
public static string mencodertool = ConfigurationManager.AppSettings["mencoder"];
public static string mplayertool = ConfigurationManager.AppSettings["mplayer"];
public static string upFile = ConfigurationManager.AppSettings["upfile"] + "/";
public static string imgFile = ConfigurationManager.AppSettings["imgfile"] + "/";
public static string playFile = ConfigurationManager.AppSettings["playfile"] + "/";
//文件图片大小
public static string sizeOfImg = ConfigurationManager.AppSettings["CatchFlvImgSize"];
//文件大小
public static string widthOfFile = ConfigurationManager.AppSettings["widthSize"];
public static string heightOfFile = ConfigurationManager.AppSettings["heightSize"];
// // //获取文件的名字
public static string GetFileName(string fileName)
{
int i = fileName.LastIndexOf("\\") + 1;
string Name = fileName.Substring(i);
return Name;
}
//获取文件扩展名
public static string GetExtension(string fileName)
{
int i = fileName.LastIndexOf(".")+1;
string Name = fileName.Substring(i);
return Name;
}
//
#region //运行FFMpeg的视频解码,(这里是绝对路径)
/// <summary>
/// 转换文件并保存在指定文件夹下面(这里是绝对路径)
/// </summary>
/// <param name="fileName">上传视频文件的路径(原文件)</param>
/// <param name="playFile">转换后的文件的路径(网络播放文件)</param>
/// <param name="imgFile">从视频文件中抓取的图片路径</param>
/// <returns>成功:返回图片虚拟地址; 失败:返回空字符串</returns>
public string ChangeFilePhy(string fileName, string playFile, string imgFile)
{
//取得ffmpeg.exe的路径,路径配置在Web.Config中,如:<add key="ffmpeg" value="E:\51aspx\ffmpeg.exe" />
string ffmpeg = Server.MapPath(PublicMethod.ffmpegtool);
if ((!System.IO.File.Exists(ffmpeg)) || (!System.IO.File.Exists(fileName)))
{
return "";
}
//获得图片和(.flv)文件相对路径/最后存储到数据库的路径,如:/Web/User1/00001.jpg
string flv_file = System.IO.Path.ChangeExtension(playFile, ".flv");
//截图的尺寸大小,配置在Web.Config中,如:<add key="CatchFlvImgSize" value="240×180" />
string FlvImgSize = PublicMethod.sizeOfImg;
System.Diagnostics.ProcessStartInfo FilestartInfo = new System.Diagnostics.ProcessStartInfo(ffmpeg);
FilestartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
FilestartInfo.Arguments = " -i " + fileName + " -ab 56 -ar 22050 -b 500 -r 15 -s " + widthOfFile + "x" + heightOfFile + " " + flv_file;
//ImgstartInfo.Arguments = " -i " + fileName + " -y -f image2 -t 0.05 -s " + FlvImgSize + " " + flv_img;
try
{
//转换
System.Diagnostics.Process.Start(FilestartInfo);
//截图
CatchImg(fileName, imgFile);
//System.Diagnostics.Process.Start(ImgstartInfo);
}
catch
{
return "";
}
//
return "";
}
//
public string CatchImg(string fileName,string imgFile)
{
//
string ffmpeg = Server.MapPath(PublicMethod.ffmpegtool);
//
string flv_img =imgFile+".jpg";
//
string FlvImgSize = PublicMethod.sizeOfImg;
//
System.Diagnostics.ProcessStartInfo ImgstartInfo = new System.Diagnostics.ProcessStartInfo(ffmpeg);
ImgstartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
//
ImgstartInfo.Arguments = " -i " + fileName + " -y -f image2 -ss 2 -vframes 1 -s " + FlvImgSize + " " + flv_img;
try
{
System.Diagnostics.Process.Start(ImgstartInfo);
}
catch
{
return "";
}
//
if (System.IO.File.Exists(flv_img))
{
return flv_img;
}
return "";
}
#endregion
//
#region //运行FFMpeg的视频解码,(这里是(虚拟)相对路径)
/// <summary>
/// 转换文件并保存在指定文件夹下面(这里是相对路径)
/// </summary>
/// <param name="fileName">上传视频文件的路径(原文件)</param>
/// <param name="playFile">转换后的文件的路径(网络播放文件)</param>
/// <param name="imgFile">从视频文件中抓取的图片路径</param>
/// <returns>成功:返回图片虚拟地址; 失败:返回空字符串</returns>
public string ChangeFileVir(string fileName, string playFile, string imgFile)
{
//取得ffmpeg.exe的路径,路径配置在Web.Config中,如:<add key="ffmpeg" value="E:\51aspx\ffmpeg.exe" />
string ffmpeg = Server.MapPath(PublicMethod.ffmpegtool);
if ((!System.IO.File.Exists(ffmpeg)) || (!System.IO.File.Exists(fileName)))
{
return "";
}
//获得图片和(.flv)文件相对路径/最后存储到数据库的路径,如:/Web/User1/00001.jpg
string flv_img = System.IO.Path.ChangeExtension(Server.MapPath(imgFile), ".jpg");
string flv_file = System.IO.Path.ChangeExtension(Server.MapPath(playFile), ".flv");
//截图的尺寸大小,配置在Web.Config中,如:<add key="CatchFlvImgSize" value="240×180" />
string FlvImgSize = PublicMethod.sizeOfImg;
System.Diagnostics.ProcessStartInfo FilestartInfo = new System.Diagnostics.ProcessStartInfo(ffmpeg);
System.Diagnostics.ProcessStartInfo ImgstartInfo = new System.Diagnostics.ProcessStartInfo(ffmpeg);
FilestartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
ImgstartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
//此处组合成ffmpeg.exe文件需要的参数即可,此处命令在ffmpeg 0.4.9调试通过
//ffmpeg -i F:\01.wmv -ab 56 -ar 22050 -b 500 -r 15 -s 320×240 f:\test.flv
FilestartInfo.Arguments = " -i " + fileName + " -ab 56 -ar 22050 -b 500 -r 15 -s " + widthOfFile + "x" + heightOfFile + " " + flv_file;
ImgstartInfo.Arguments = " -i " + fileName + " -y -f image2 -t 0.001 -s " + FlvImgSize + " " + flv_img;
try
{
System.Diagnostics.Process.Start(FilestartInfo);
System.Diagnostics.Process.Start(ImgstartInfo);
}
catch
{
return "";
}
/**/
///注意:图片截取成功后,数据由内存缓存写到磁盘需要时间较长,大概在3,4秒甚至更长;
///这儿需要延时后再检测,我服务器延时8秒,即如果超过8秒图片仍不存在,认为截图失败;
///此处略去延时代码.如有那位知道如何捕捉ffmpeg.exe截图失败消息,请告知,先谢过!
if (System.IO.File.Exists(flv_img))
{
return flv_img;
}
return "";
}
#endregion
#region //运行mencoder的视频解码器转换(这里是(绝对路径))
public string MChangeFilePhy(string vFileName, string playFile, string imgFile)
{
string tool = Server.MapPath(PublicMethod.mencodertool);
//string mplaytool = Server.MapPath(PublicMethod.ffmpegtool);
if ((!System.IO.File.Exists(tool)) || (!System.IO.File.Exists(vFileName)))
{
return "";
}
string flv_file = System.IO.Path.ChangeExtension(playFile, ".flv");
//截图的尺寸大小,配置在Web.Config中,如:<add key="CatchFlvImgSize" value="240×180" />
string FlvImgSize = PublicMethod.sizeOfImg;
System.Diagnostics.ProcessStartInfo FilestartInfo = new System.Diagnostics.ProcessStartInfo(tool);
FilestartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
FilestartInfo.Arguments = " " + vFileName + " -o " + flv_file + " -of lavf -lavfopts
i_certify_that_my_video_stream_does_not_use_b_frames -oac mp3lame -lameopts abr:br=56 -ovc lavc
-lavcopts vcodec=flv:vbitrate=200:mbd=2:mv0:trell:v4mv:cbp:last_pred=1:dia=-1:cmp=0:vb_strategy=1
-vf scale=" + widthOfFile + ":" +heightOfFile + " -ofps 12 -srate 22050";
try
{
System.Diagnostics.Process.Start(FilestartInfo);
CatchImg(flv_file, imgFile);
}
catch
{
return "";
}
//
return "";
}
#endregion
}

[Flex]Flex资源站推荐

mikel阅读(795)

一、国外站点
1.资源
Adobe Flex 2 Component Explorer : 官方的,展示了各种组件用法入门 必看。
CFlex :很好的一个Flex资源站点,包括教程 ,新闻,资源站点…… 只是页面有点杂乱,大家一般看右边那一栏就行了。
FlexBox :一个收集 了网上很多开源 组件的站点,是进阶学习 的好帮手。
FlexLib :也是一个开源Flex组件站点,不过与FlexBox不同的是,这个是原创,而FlexBox只是收集。
Flex Developer Center :Adobe Flex开发 者中心,经常会有一些好的教程出现。
Adobe Labs :这个不用我说了吧。
Flex.org:http://www.flex.org/ 官方的,基本上应有尽有。
2. Explorers
Flex 2 Style Explorer :用来设计程序 样式风格的工具,很好用,现在源代码 已经可以下载
Flex 2 Primitive Explorer :用来调节各种Primitive图形的组件,非官方的,源代码提供下载。
Flex 2 Filter Explorer :用来调节各种滤镜(filter),非官方的,源代码提供下载。
3. Blogs
MXNA :这个不用我说了吧,虽说这不是一个Blog,但是它聚合了所有优秀的Blog,所以把它放在Blog一栏,下面所有的Blog都在这个聚合中。
Alex Uhlmann:http://web logs.macromedia.com/auhlmann/
Christophe Coenraets:http://coenraets.org/ 特别推荐
Code Slinger:http://blogs.digitalprimates.net/codeSlinger/
Deitte:http://www.deitte.com/
Doug mccune:http://dougmccune.com/blog/ 特别推荐
Flex Doc Team:http://blogs.adobe.com/flexdoc/
Kuwamoto:http://kuwamoto.org/ 特别推荐
Macromedia Consulting:http://weblogs.macromedia.com/mc/
Matt Chotin:http://weblogs.macromedia.com/mchotin/ 特别推荐
Peter Ent:http://weblogs.macromedia.com/pent/ 特别推荐
Quietly Scheming:http://www.quietlyscheming.com/blog/ 特别推荐
ScaleNine Blog:http://www.scalenine.com/blog/index.php 特别推荐
Steven Webster:http://weblogs.macromedia.com/swebster/
EverythingFlex:http://blog.everythingflex.com/ 特别推荐
Alex’s Flex Closet:http://blogs.adobe.com/aharui/ 特别推荐
4. 邮件 列表
FlexCoders:http://tech.groups.yahoo.com/group/flexcoders/
Flex Components:http://www.adobe.com/go/flexcomponents 非高级开发者最好别加入
上面是两个比较有名的邮件列表,建议大家提问之前先搜索一下邮件存档,一般都能找到答案,找不到再提问。更多邮件列表请看这里:http://flex.org/community/
5.Cairngorm 相关
Cairngorm Documentation Group 这个里面收集了基本上所有关于Cairngorm的资料
二、国内站点
1.论坛
RIACHINA :前身是RIACN,国内最好的Flex论坛之一。我最初知道Flex从这里开始,对这个站挺有感情,饮水思源,把它排第一。
AnyFlex :国内最好的Flex论坛之一,成立的时间比较早,而且论坛FTP中有很多好的资料。
JavaUU : 后起之秀,主要收集一些java的开源技术 及热闹技术 资源,特别是FLEX的资源相当齐全;
RIADev :Google网上论坛,d.CAT前辈主持的,一般小问题都能解决。
FlexCoders.cn :刚起步的论坛,不过看域名觉得挺有前途,呵呵。
2.Blogs
Y.X.Shawn :对Flex研究很深入,自己写一些开源的组件。
d.CAT :高级开发者,台湾的,为数不多的华语高级开发者,他还做过一个类似Caringorm的架构。
Kenshin :很早就开始研究Flex了,自己开发过很多东西。