[转载]关于Android系统微博服务端API的OAuth认证实现

mikel阅读(1608)

[转载]关于微博服务端API的OAuth认证实现 – 遇见未知的自己 – 博客园.

新浪微博跟update相关的api已经挂了很多天了一直没有恢复正常,返回错误:40070 Error limited application access api!,新浪开放平台的论坛里n多的人都在等这个恢复,新浪官方也相当的恶心出问题了连个公告都没有,既不说什么原因又不说什么时候能恢复,。还是有版主说是api正在升级礼拜1恢复正常今天都礼拜2了还是不行。基于这个原因我的Android版的新浪微博客户端已经停工好几天了,刚好是跟update相关的一些功能。

客户端开发不成了,就自己做做服务端程序,提供类似新浪微博rest api服务, api其实说简单也很简单了,无法是通过链接对外提供json或者xml格式的数据和接收外部提供的数据进去相应的存储、删除、更新等操作。过程中碰到的最麻烦的问题就是OAuth认证功能了,在做Android版的新浪微博客户端时候也花了蛮长的时间对OAuth认证进行研究,在客户端原先是采用oauth-signpost开源项目,后来由于某些原因就放弃了这个开源类库,自己重新写了OAuth认证部分的实现, 现在做服务端的OAuth认证,其实有过做客户端的经验做服务端也差不多,简单的说无非是客户端对参数字符串进行签名然后把签名值传输到服务端,服务端也对同样对参数字符串进行签名,把从客户端传过来的签名值进去比较,简单的说就这么个过程,具体实现肯定比这个要复杂多了,不明真相的同学可以google一下OAuth进行深入的学习研究了。

服务端程序用ASP.NETC#编写了而非java,理由很简单本人对.net更加熟悉。由于想快速的实现效果采用了oauth-dot-net开源项目并没有全部自己写。

一、首先新建名为Rest Api的ASP.NET Web应用程序,然后添加 oauth-dot-net开源项目相关的几个dll(Castle.Core.dll、 Castle.MicroKernel.dll、Castle.Windsor.dll、 CommonServiceLocator.WindsorAdapter.dll、 Microsoft.Practices.ServiceLocation.dll、OAuth.Net.Common.dll、 OAuth.Net.Components.dll、OAuth.Net.ServiceProvider.dll)。

二、在Web.config文件里添加相应的配置,具体可以参考OAuth.Net.Examples.EchoServiceProvider项目,然后在Global.asax.cs添加如下代码:

public override void Init()
{
IServiceLocator injector
=
new WindsorServiceLocator(
new WindsorContainer(
new XmlInterpreter(
new ConfigResource(oauth.net.components))));

ServiceLocator.SetLocatorProvider(() => injector);

}

接下来是比较重要,就是request_token、authorize、access_token的实现,OAuth认证实现的几个过程,不理解可以看android开发我的新浪微博客户端-OAuth篇(2.1) ,具体代码实现很多是参考OAuth.Net.Examples.EchoServiceProvider示例项目。

三、 首先新建ConsumerStore.cs类,用来存储Consumer信息,由于测试项目所以存储在内存中并没有考虑保存到数据库,真实项目的时候请把相应的Consumer信息保存到数据库中。Consumer信息对应新浪微博其实就是应用的App Key和App Secret,当开发者在新浪微博建一个新的应用获取App Key和App Secret,所以完整的应该还需要一个开发一个提供给第三方开发者申请获取App Key和App Secret的功能页面,这里就不具体实现,直接在代码里写死了一个名为测试应用的Consumer,App Key:2433927322,App Secret:87f042c9e8183cbde0f005a00db1529f,这个提供给客户端测试用。 具体代码如下:

public sealed class ConsumerStore : InMemoryConsumerStore, IConsumerStore
{
internal static readonly IConsumer FixedConsumer = new OAuthConsumer(2433927322, 87f042c9e8183cbde0f005a00db1529f, 测试应用, ConsumerStatus.Valid);

public ConsumerStore()
{
this.ConsumerDictionary.Add(
ConsumerStore.FixedConsumer.Key,
ConsumerStore.FixedConsumer);
}

public override bool Add(IConsumer consumer)
{
throw new NotSupportedException(Consumers cannot be added to this store–it is fixed.);
}

public override bool Contains(string consumerKey)
{
return ConsumerStore.FixedConsumer.Key.Equals(consumerKey);
}

public override bool Update(IConsumer consumer)
{
throw new NotSupportedException(Consumers cannot be updated in this store–it is fixed.);
}

public override bool Remove(IConsumer consumer)
{
throw new NotSupportedException(Consumers cannot be removed from this store–it is fixed.);
}

}

四、接下来就是request_token功能,新建 RequestTokenHandler.cs ,这个是OAuth.Net.ServiceProvider.RequestTokenHandler 子类,并且是httpHandlers所以需要在Web.config中添加httpHandlers配置,这个用来接收客户端程序的请求,返回给客户端 程序Request Token和Request Secret用,具体代码如下:

public sealed class RequestTokenHandler : OAuth.Net.ServiceProvider.RequestTokenHandler
{
protected override void IssueRequestToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
//产生RequestToken
IRequestToken token = this.GenerateRequestToken(httpContext, requestContext);

requestContext.RequestToken = token;
Uri callbackUri;
if (Uri.TryCreate(requestContext.Parameters.Callback, UriKind.Absolute, out callbackUri))
{
if (!ServiceProviderContext.CallbackStore.ContainsCallback(token))
{
//保存Callback地址了
ServiceProviderContext.CallbackStore.AddCallback(token, callbackUri);
}
}
else
OAuthRequestException.ThrowParametersRejected(
new string[] { Constants.CallbackParameter }, Not a valid Uri.);

//把token.Token和token.Secret输出到客户端,
requestContext.ResponseParameters[Constants.TokenParameter] = token.Token;
requestContext.ResponseParameters[Constants.TokenSecretParameter]
= token.Secret;
}

protected override IRequestToken GenerateRequestToken(HttpContext httpContext, OAuthRequestContext requestContext)
{

return ServiceProviderContext.TokenGenerator.CreateRequestToken(requestContext.Consumer, requestContext.Parameters);
}

}

五、 接着是authorize功能,新建名为 authorize.aspx的页面,用来给用户输入账号和密码进行授权的页面,这个页面很简单具体如下图,在这个页面中获取用户输入的账户和密码跟数据 库中存储的用户账号和密码进行验证,如果验证通过返回之前客户端提供的callback地址,并且给这个地址添加一个校验码,具体代码如下:

public partial class authorize : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}

protected void Button1_Click(object sender, EventArgs e)
{
if (loginName.Text == test && password.Text == 123)
{
string toke = Request.Params[oauth_token];
IRequestToken tk
= ServiceProviderContext.TokenStore.GetRequestToken(toke);
Uri callback
= ServiceProviderContext.CallbackStore.GetCalback(tk);
string oauth_verifier = ServiceProviderContext.VerificationProvider.Generate(tk);
Response.Redirect(callback.ToString()
+ ?oauth_verifier= + oauth_verifier);
}

}

}

六、接下来就是access_token功能,新建AccessTokenHandler.cs , 这个是OAuth.Net.ServiceProvider.AccessTokenHandler子类,并且是httpHandlers所以需要在Web.config中添加httpHandlers配置,这个用来接收客户端程序的请求,返回给客户端程序Access Token和Access Secret用,具体代码如下:

public sealed class AccessTokenHandler : OAuth.Net.ServiceProvider.AccessTokenHandler
{
protected override void IssueAccessToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
//产生access token
IAccessToken accessToken = this.GenerateAccessToken(httpContext, requestContext);

accessToken.Status = TokenStatus.Authorized;

// 把accessToken和accessSecret输出到客户端,
requestContext.ResponseParameters[Constants.TokenParameter] = accessToken.Token;
requestContext.ResponseParameters[Constants.TokenSecretParameter]
= accessToken.Secret;
}

protected override IAccessToken GenerateAccessToken(HttpContext httpContext,  OAuthRequestContext requestContext)
{
return ServiceProviderContext.TokenGenerator.CreateAccessToken(requestContext.Consumer, requestContext.RequestToken);
}
}

public class TokenGenerator : ITokenGenerator
{
internal static readonly IRequestToken FixedRequestToken = new OAuthRequestToken(requestkey,
requestsecret,
ConsumerStore.FixedConsumer,
TokenStatus.Authorized,
null,
ServiceProviderContext.DummyIdentity,
new string[] { });

internal static readonly IAccessToken FixedAccessToken = new OAuthAccessToken(
accesskey,
accesssecret,
ConsumerStore.FixedConsumer,
TokenStatus.Authorized,
TokenGenerator.FixedRequestToken);

public IRequestToken CreateRequestToken(IConsumer consumer, OAuthParameters parameters)
{
return TokenGenerator.FixedRequestToken;
}

public IAccessToken CreateAccessToken(IConsumer consumer, IRequestToken requestToken)
{
return TokenGenerator.FixedAccessToken;
}
}

public class TokenStore : InMemoryTokenStore, ITokenStore
{
public TokenStore()
{
this.RequestTokenDictionary.Add(
TokenGenerator.FixedRequestToken.Token,
TokenGenerator.FixedRequestToken);

this.AccessTokenDictionary.Add(
TokenGenerator.FixedAccessToken.Token,
TokenGenerator.FixedAccessToken);
}

public override bool Add(IRequestToken token)
{
throw new NotSupportedException(Tokens cannot be added to the token store–it is fixed.);
}

public override bool Add(IAccessToken token)
{
throw new NotSupportedException(Tokens cannot be added to the token store–it is fixed.);
}

public override bool Update(IRequestToken token)
{
throw new NotSupportedException(Tokens cannot be updated in the token store–it is fixed.);
}

public override bool Update(IAccessToken token)
{
throw new NotSupportedException(Tokens cannot be updated in the token store–it is fixed.);
}

public override bool Remove(IRequestToken token)
{
throw new NotSupportedException(Tokens cannot be removed from the token store–it is fixed.);
}

public override bool Remove(IAccessToken token)
{
throw new NotSupportedException(Tokens cannot be removed from the token store–it is fixed.);
}

}

这样就完成了一个最最简单小型的服务端OAuth认证,然后用android客户端进行测试ok通过。

注意点:

一、android模拟器访问本地服务地址为10.0.2.2,比如http://localhost:3423/authorize.aspx在模拟器中用http://10.0.2.2:3423/authorize.aspx。

二、OAuth.Net类库的OAuth.Net.Common项目中的interface ICallbackStore 添加了一个Uri GetCalback(IRequestToken token);并且在具体的实现类InMemoryCallbackStore添加了实习代码:

public Uri GetCalback(IRequestToken token)

{
lock (this.callbackStore)
{
if (this.callbackStore.ContainsKey(token))
{
return this.callbackStore[token];
}
else
{
return null;
}
}
}

三、为了能用我前面做的给新浪用的android客户端,对于类库源代码AccessTokenHandler的ParseParameters方法做了如下修改,因为新浪请求api的时候都会加一个source的参数:

protected virtual void ParseParameters(HttpContext httpContext, OAuthRequestContext requestContext)

{
…….
parameters.AllowOnly(
Constants.ConsumerKeyParameter,
Constants.TokenParameter,
Constants.SignatureMethodParameter,
Constants.SignatureParameter,
Constants.TimestampParameter,
Constants.NonceParameter,
Constants.VerifierParameter,
Constants.VersionParameter, // (optional)
Constants.RealmParameter, // (optional)
“source”);
……
}

[转载]百度地图API建立全国银行位置查询系统(三)——如何在地图上添加标注

mikel阅读(1096)

[转载]【百度地图API】建立全国银行位置查询系统(三)——如何在地图上添加标注 – 酸奶小妹 – 博客园.

<摘要>你将在第三章中学会以下知识:

  1. 如何在地图上添加带银行logo的标注?(你也可以换成商场logo,酒店logo等)
  2. 如何在标注上显示信息窗口,以及添加文字标签等其他覆盖物;
  3. 最后,介绍一个获取坐标的给力工具。

—————————————————————————————————————–

一、如何添加标注、标签和信息窗口?

首先,我们需要创建一个点坐标,利用该点坐标来创建一个标注(Maker),最后将该标注显示在地图上。

比如我们了解到国家博物馆的坐标是116.407804,39.912123(如何获取这个坐标,请查看下文中的坐标拾取工具)。

所以创建这个点,再创建一个标注在这个点上。最关键的一步就是将它显示出来,显示覆盖物用addOverlay。

var pointMarker = new BMap.Point(116.407804,39.912123); // 创建标注的坐标 var marker = new BMap.Marker(pointMarker); // 创建标注 map.addOverlay(marker); // 将标注添加到地图中

接下来,我们需要在这个标注的基础上,添加一个信息窗口。

为了在标注上添加一个信息窗口,我们需要对该标注建立一个监听事件,当鼠标点击标注后,方能显示信息窗口。

添加事件请使用addEventListener.

var infoWindow = new BMap.InfoWindow("点击标注后信息窗口就显示了"); // 创建信息窗口对象 marker.addEventListener("click", function(){ //给标注添加点击事件 this.openInfoWindow(infoWindow); });

同理,你可以自己添加文字标签label。

其中point是文字标签显示的位置,offset可以设置它的偏移量。{}里的东西默认是可以不写的。

var label = new BMap.Label("请点击红色标注",{point : pointMarker, 
        offset: new BMap.Size(3,-6)}); //定义一个文字标签 map.addOverlay(label);

点击这里,运行该代码。右键点击新打开的窗口,可以查看源代码哦。

二、如何添加银行的标注?

但是,大家会不会觉得默认的这个红色标注很丑呢?那么我们一起来换一个标注吧!下面开始讲解,如何自定义标注。

为了看清楚,我把标注放得比较大。你自己可以换张小图片试试。

首先,我们要准备的是一张标注的图片。注意,一定需要一张背景透明的图片。然后定义三家银行的标注样式。有4个地方需要我们来设置。

第一就是银行图标的地址,我们可以把多张银行图标放在一张图上。比如这张图,点击这里下载。

第二是这个标注的大小BMap.Size,需要根据你银行图标的大小来定。

第三个是标注的偏移量offset。为什么要有偏移量呢?因为我们希望图标下面那个小尖尖刚好指在我们需要的坐标点上。

第四个就是相当于CSS sprites的设置了。由于我们的银行图标都放在了同一张图上,所以需要靠imageOffset这个设置来调整显示位置。

具体代码如下:

// 创建招商银行的标注图标 var zsIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif", //图片地址 new BMap.Size(40, 64), // 标注显示大小 { offset: new BMap.Size(20, 64), // 标注底部小尖尖的偏移量 imageOffset: new BMap.Size(0, 0) // 这里相当于CSS sprites }); // 创建中国银行的标注图标 var zgIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif", //图片地址 new BMap.Size(40, 64), // 标注显示大小 { offset: new BMap.Size(20, 64), // 标注底部小尖尖的偏移量 imageOffset: new BMap.Size(0, -64) // 这里相当于CSS sprites }); // 创建建设银行的标注图标 var jsIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif", //图片地址 new BMap.Size(40, 64), // 标注显示大小 { offset: new BMap.Size(20, 64), // 标注底部小尖尖的偏移量 imageOffset: new BMap.Size(0, -128) // 这里相当于CSS sprites });

然后我们分别添加3个银行标注在地图上。点击这里运行代码。源代码如下:

<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>酸奶小妹——百度地图API学习</title> <style type="text/css"> html{height:100%} body{height:100%;margin:0px;padding:0px} #milkMap{height:400px;width:600px;border:1px solid blue;} </style> <script type="text/javascript" src="http://api.map.baidu.com/api?v=1.1&services=false"></script> </head> <body> <div id="milkMap"></div> </body> <script type="text/javascript"> var map = new BMap.Map("milkMap"); // 创建地图实例 var point = new BMap.Point(116.404, 39.915); // 创建点坐标 map.centerAndZoom(point, 16); // 初始化地图,设置中心点坐标和地图级别 map.addControl(new BMap.OverviewMapControl({isOpen: 1, anchor: BMAP_ANCHOR_TOP_RIGHT})); //为地图添加鹰眼 map.addControl(new BMap.NavigationControl()); //为地图添加鱼骨 var myCopyright = new BMap.CopyrightControl({offset: new BMap.Size(485, 0)}) //设置版权信息偏移量 map.addControl(myCopyright); //为地图添加版权控件 myCopyright.addCopyright({id : 1, content : '<a style="line-height:30px;height:30px;display:block;color:red;background:yellow" href="http://www.cnblogs.com/milkmap/"><img src="http://www.ui-love.com/static/img/uiico.ico" />酸奶小妹的博客园</a>'}); // 创建招商银行的标注图标 var zsIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif", //图片地址 new BMap.Size(40, 64), // 标注显示大小 { offset: new BMap.Size(20, 64), // 标注底部小尖尖的偏移量 imageOffset: new BMap.Size(0, 0) // 这里相当于CSS sprites }); // 创建中国银行的标注图标 var zgIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif", //图片地址 new BMap.Size(40, 64), // 标注显示大小 { offset: new BMap.Size(20, 64), // 标注底部小尖尖的偏移量 imageOffset: new BMap.Size(0, -64) // 这里相当于CSS sprites }); // 创建建设银行的标注图标 var jsIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif", //图片地址 new BMap.Size(40, 64), // 标注显示大小 { offset: new BMap.Size(20, 64), // 标注底部小尖尖的偏移量 imageOffset: new BMap.Size(0, -128) // 这里相当于CSS sprites }); var pointMarker1 = new BMap.Point(116.403704,39.912123); // 创建招商银行标注的坐标 var pointMarker2 = new BMap.Point(116.407804,39.916123); // 创建中国银行标注的坐标 var pointMarker3 = new BMap.Point(116.400804,39.915123); // 创建建设银行标注的坐标 var marker1 = new BMap.Marker(pointMarker1, {icon: zsIcon}); // 创建招商银行标注 var marker2 = new BMap.Marker(pointMarker2, {icon: zgIcon}); // 创建中国银行标注 var marker3 = new BMap.Marker(pointMarker3, {icon: jsIcon}); // 创建建设银行标注 map.addOverlay(marker1); // 将招商银行标注添加到地图中 map.addOverlay(marker2); // 将中国银行标注添加到地图中 map.addOverlay(marker3); // 将建设银行标注添加到地图中 var infoWindow1 = new BMap.InfoWindow("你点击了招商银行的标注",{offset: new BMap.Size(0, -64)}); marker1.addEventListener("click", function(){ //给招商银行标注添加点击事件 this.openInfoWindow(infoWindow1); //打开招商银行的窗口 }); var infoWindow2 = new BMap.InfoWindow("你点击了中国银行的标注",{offset: new BMap.Size(0, -64)}); marker2.addEventListener("click", function(){ //给中国银行标注添加点击事件 this.openInfoWindow(infoWindow2); //打开中国银行的窗口 }); var infoWindow3 = new BMap.InfoWindow("你点击了建设银行的标注",{offset: new BMap.Size(0, -64)}); marker3.addEventListener("click", function(){ //给建设银行标注添加点击事件 this.openInfoWindow(infoWindow3); //打开建设银行的窗口 }); </script> </html>

——————————————————————————————————————–

小贴士:什么是覆盖物?

答:覆盖物包括了标注Marker、文字标签Label、信息窗口InfoWindow、圆形Circle、多边形Polygon,以及折线Polyline。

添加任何一个覆盖物都需要map.addOverlay();这个函数。

如何添加其他覆盖物呢?你可以到API的官方网站上查询类参考->覆盖物

——————————————————————————————————————–

三、找坐标的使用工具——坐标拾取系统

1、进入该工具网址:http://openapi.baidu.com/map/pick/index.html

2、在搜索框中输入你想查询位置的中文名称,例如“安定门”

3、找到合适的位置,点击鼠标右键开启添加标注功能。(小窍门:地图级别越高,位置越精确,建议把地图级别开到17或者18级哦~)

4、开启功能后,你就可以左键点击地图了。无论你点在哪里,网页的右边都会出现一组坐标信息,这就是当前小红点的坐标了。

5、如果你想改变小红点的位置,可以直接点击地图上另外的点,也可以拖动小红点。

注意:如果你要拖动地图,请先点击鼠标右键,关闭添加标注的功能。

———————————————————————————————————————–

学到这里,大家已经可以动手自己做一张自定义的银行分布图了。

主要步骤:

1、利用工具找到银行的地理位置,也就是坐标。

2、把银行名称、地址、坐标等内容存入数据库。

3、创建一个搜索条,查询用户输入的关键字。比如,用户输入“大望路 招商银行”。

4、查询数据库,找到数据2符合条件。于是在(106.40,30.91)这个点上,添加一个招商银行的标注。

注意:这里显示的标注是自定义标注,就是说,如果查询的是招商银行,就显示招商银行的标注;查询的是建设银行,就显示建设银行的标注。

至于怎样建立数据库,我这里就不多说了,因为不太涉及到GIS或者API的东西。

有兴趣的同学可以去学习一下数据库,还有PHP或者.NET等后台程序。

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

如果我们没有自己的数据库,也想建立一个银行网点的分布图,应该如何做呢?

在下一章,我们将学会,如何利用百度地图的数据库来建立全国银行网点的查询地图。

[转载]最终用户在线设计和修改Web报表

mikel阅读(1201)

[转载]最终用户在线设计和修改Web报表 – Web打印 – 博客园.

我在最近主持开发的人力资源管理系统中遇到一个新的问题:就是最终用户要能够设计和编辑报表。在人力资源管理系统中有一个基本的功能,就是编辑和打印员工 的工作证,不同职务的员工的工作证可能不一样,同一个员工不同工作时期(比如实习员工、正式员工)的工作证也可能不一样。项目方要求我们一定要做到操作员 可以在线设计和修改工作证的格式(实际上就是报表),否则就一切免谈。客户是上帝!呵呵,努力做吧。于是大家搜罗了所有的Web打印软件,结果包括大名鼎 鼎的水晶报表也没有这个功能。
好在天无绝人之路,我在以前所设计的一个基于FastReport的Web打印控件之上进行了大量的修改,终于实现了最终用户的在线设计和编辑报表的功能,用户也比较满意。
报表的预览窗口如下图:

报表的最终用户在线设计和编辑窗口如下图:

当然报表的在线编辑窗口有一点专业,操作员需要经过一点培训才会使用,不过一般情况是我们先帮用户设计好了模板,操作员平时只是要做一些微调,比如改个字或改个格式等。报表编辑好后,点击就可以自动把报表保存到服务器,且提示
下面详细的讲解一下怎样利用我设计的Web打印控件实现最终用户的在线设计和修改报表,且自动把报表保存至服务器。讲解以ASP.NET为例,当然其它语 言也可以举一反三,很容易实现。因为调用打印控件统一是JavaScirpt,提交服务器的数据是Http协议的Post方式,这些都是通用的。
最终用户在线设计和修改Web报表的部分代码如下:

调用Web打印控件

protected void BtnDepsitAmtDesignPost_Click(object sender, EventArgs e) { string FileValue, PrintValue, ParaName, ParaValue; FileValue = FileToString(".\\Frp\\DepositAmt.fr3"); ParaName = "ShopName`~PrintDepositAdd`~PrintPaperNo`~Title"; //`~为各参数的分隔符 ParaValue = "测试酒楼" + "`~说明:本单据为贵客押金收取凭证,盖章有效。退房时请出示,遗失者自负,请妥善保存。退房时间为12:00时,延时退房18:00时以前按半天房费收取,18:00时以后算全天房价。押金单有效期为一个月,过期作废。 贵重物品请交前台寄存,未寄存丢失自负。 谢谢!" + "`~身份证:4325011980639512" + "`~押金单"; SqlConnection ConPrintTest = new SqlConnection(ConfigurationManager.ConnectionStrings["PrintTestConnectionString"].ToString()); ConPrintTest.Open(); DataSet DsCashLog = new DataSet(); SqlDataAdapter DaCashLog = new SqlDataAdapter("Select top 1 CashNo, CashDate, CashAmt, PayName, GuestName, RoomNo, ItemRemark, CashUserName, Remark From CashLog", ConPrintTest); DaCashLog.FillSchema(DsCashLog, SchemaType.Source, "CashLog"); DaCashLog.Fill(DsCashLog, "CashLog"); PrintValue = TableToXml(DsCashLog.Tables["CashLog"]); DsCashLog.Dispose(); ConPrintTest.Close(); string ScriptStr; ScriptStr = "<script language='javascript'>window.onload = function() { try { var ObjPrintMange = new ActiveXObject('WebPrint.WebPrintUnit'); } catch(e) { if( confirm('打印控件未安装,现在下载吗?') ) { window.location='./PrintActivex.exe'; } return; } var OldVersion=ObjPrintMange.Version; NewVerion='3.5(2011-02-17)'; if(OldVersion < NewVerion) { ObjPrintMange = null; alert('打印控件需升级。请先进行下载,下载后关闭IE,然后安装升级版。'); window.location='./PrintActivex.exe'; return; } " + " ObjPrintMange.CheckReg('公司名称', '3B8E5B998A3125EE89983EA940BB2AEE'); " //注册码 + " ObjPrintMange.ReportFileName='DepositAmt.fr3'; " + " ObjPrintMange.PostURL='http://www.xinyuerj.com/ASPPost/Show.asp?FileName=DepsitAmt.fr3'; " + " ObjPrintMange.DesignReport('" + FileValue + "' , '" + ParaName + "', '" + ParaValue + "', '" + PrintValue + "', '', '', '', '', '');" + "ObjPrintMange = null; } </script>"; ScriptStr = ScriptStr.Replace(System.Environment.NewLine, string.Empty); Response.Write(ScriptStr); }

报表控件在以前的基础上增加了PostURL属性,设置此属性,则在报表在线编辑时,单击“保存”按钮或“保存”菜单项,则把报表内容组织为 String,且通过Http的Post方式直接提交到所设置的URL页面,用户可以在URL接收报表内容,把报表内容保存在指定文件或数据库中。 Post的字段名指定为ReportFileValue,用户可以通过接收此字段的值保存在线编辑的报表内容。报表的内容为String,打印或预览时可 以直接调用此内容,无需用FileToStr函数进行转换。
报表控件的DesignReport函数,功能为在线编辑报表,参数:报表文件字符串,报表参数名称字符串,报表参数值字符串,数据集1的字符串,数据集 2的字符串,数据集3的字符串,数据集4的字符串,数据集5的字符串,数据集6的字符串。报表文件字符串调用FileToStr函数产生;报表参数名称字 符串为报表中所使用的参数的名称,各名称之间以`~分隔;报表参数值字符串为报表中所使用的参数所对应的值,各参数值之间以`~分隔;数据集的字符串通过 调用FileToStr函数产生,若没有数据则为空。
作为接收Http的Post数据的服务器的页面设计如下:

接收提交的报表内容

public partial class Show : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string FileName, FileValue; FileName = Request.QueryString["FileName"]; FileValue = Request.Form["ReportFileValue"]; WriteReportFile(FileName, FileValue); Response.Write("File Name" + FileName); Response.Write("File Value" + FileValue); } private void WriteReportFile(string FileName, string FileValue) { if ( FileName == null || FileName == "" || FileValue == null ||FileValue == "") return; File.WriteAllText(Server.MapPath(FileName), FileValue); } }

FileName是PostURL中所设置的保存的报表文件名,ReportFileValue则为报表文件的内容。
如果你的项目也需要最终用户在线设计和修改报表,我所设计这个Web打印控件是一种比较快捷方便的解决方法。Web打印控件目前对于个人学习研究是免费注册的,有什么问题请加入QQ群:135506194或者请直接Q我:12988672。
报表预览和编辑的网址:http://www.xinyuerj.com/ASP/
http://www.xinyuerj.com/ASP.NET/

[原创]听客户说然后再做开发

mikel阅读(1334)

不一定什么事儿都想好了才开始做,不一定什么情况都考虑到了才开始!因为不变只有变化!既然我们不能预见未来,那就适应未来的变化才是正道

—这才是真正的开发思路

最近一直在改版网站,原本以为需要考虑周全的东西以及流程在真正的客户到来后才发现都是浮云!

客户不要求你把不可预料的神马情况都考虑好了再做,而是要求你立刻马上赶快做出一个网站来用,因为客户也不知道网站将来需要什么功能,他只知道现在我要做个在线购物网站,浏览者能够通过商城买东西,下订单就行了!至于神马库存管理、销售管理、物流管理不用考虑,客户都不知道这个网站将来怎么处理这些问题,所以只需要有个可以查看前台订单的列表,知道谁买的东西,联系电话和送货地址就行了!想其他的都是多余!

客户说了等到时候遇到问题再解决问题,然后需要开发功能就开发功能,需要人工处理就人工处理,一切走着看!

做了这么多年开发,今天突然好像突然被一个雷给击中了脑袋,开窍了!这才是真理啊!原理我一直用开发者的思路去思考问题,把简单的东西复杂化了!想得太多了!

[转载]使用IntelliTrace调试跟踪ASP.NET MVC框架Action调用

mikel阅读(1073)

[转载]使用IntelliTrace调试跟踪MVC框架Action调用 – lipan – 博客园.

IntelliTrace调试跟普通断点加单步跟踪模式的区别在于,它支持对历史过程的模拟重新调试。当我们在普通调试下想了解应用程序曾经的 执行情况,一般情况下我们会停止调试,重新加断点启动调试。而有了IntelliTrace之后,我们可以用其独有的历史调试功能“回到过去”,这样一次 调试就可以有效定位问题。现在我要用这个功能,在开源MVC框架中寻找控制器的Action方法是如何被调用的。

大家都知道,MVC通过URL路由截获地址栏参数获取Controller和Action的值,并通过这两个两个字符串型的去定位控制器和控制 器的方法,再由这个方法返回视图。可问题在于,只知道字符串的类名和方法名是没有办法直接实现类并调用方法的。于是“很自然的”就想到了反射。由于反射的 性能代价太大,很多人就开始抱怨,微软的新特性都是以牺牲性能为代价的,C#是性能低下的语言。然而事实是什么样呢,话说没有调查就没有发言权,我们先展 开调查。

将MVC开源文件引入项目

1. 下载MVC框架源码: ASP.NET MVC 2 源码 ASP.NET MVC 3 源码 ,本文用的是MVC2。

2. 在VS2010新建一个MVC项目,删除引用“System.Web.Mvc”。将源码包解压,将src下SystemWebMvc目录拷贝至项目文件夹,在解决方案中添加项目,再添加对这个开源项目的引用。

点击下载配置好的项目

使用IntelliTrace调试

第一步,由于IntelliTrace调试默认是未启用的,首先你要开启它。安F5进入调试状态,看到右边的“IntelliTrace”窗 口,单击打开IntelliTrace设置按钮。勾选“启用IntelliTrace”,单选组合点选“IntelliTrace事件和调用信息”,如下 图所示。

图1

配置好后点确定,然后停止调试,在HomeController的Index方法处加以断点,启动调试,程序请求Index页面命中断点,这时你会发现围绕断点处多了几个箭头符号,这就是IntelliTrace调试要用到的。

好了,现在我们就要展示IntelliTrace调试的神奇之处了。由于我们想知道Index方法到底被谁调用了,我们怎么操作?见证奇迹的时刻就要到了!我 们要让程序倒着执行,是不是就很容易知道它的调用者?看到有个向上的双箭头,我叫他“单步回退”,单击一次,程序定位到了某个类的Execute方法中, 它调用了名为_executor委托,然后我们分析代码发现这个委托在其下方的GetExecutor函数中被实现,我们重点关注 GetExecutor,我将其贴在下面。

01 private static ActionExecutor GetExecutor(MethodInfo methodInfo) {
02 // Parameters to executor
03 ParameterExpression controllerParameter = Expression.Parameter(typeof(ControllerBase), "controller");
04 ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
05
06 // Build parameter list
07 List<Expression> parameters = new List<Expression>();
08 ParameterInfo[] paramInfos = methodInfo.GetParameters();
09 for (int i = 0; i < paramInfos.Length; i++) {
10 ParameterInfo paramInfo = paramInfos[i];
11 BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
12 UnaryExpression valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
13
14 // valueCast is "(Ti) parameters[i]"
15 parameters.Add(valueCast);
16 }
17
18 // Call method
19 UnaryExpression instanceCast = (!methodInfo.IsStatic) ? Expression.Convert(controllerParameter, methodInfo.ReflectedType) : null;
20 MethodCallExpression methodCall = methodCall = Expression.Call(instanceCast, methodInfo, parameters);
21
22 // methodCall is "((TController) controller) method((T0) parameters[0], (T1) parameters[1], ...)"
23 // Create function
24 if (methodCall.Type == typeof(void)) {
25 Expression<VoidActionExecutor> lambda = Expression.Lambda<VoidActionExecutor>(methodCall, controllerParameter, parametersParameter);
26 VoidActionExecutor voidExecutor = lambda.Compile();
27 return WrapVoidAction(voidExecutor);
28 }
29 else {
30 // must coerce methodCall to match ActionExecutor signature
31 UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
32 Expression<ActionExecutor> lambda = Expression.Lambda<ActionExecutor>(castMethodCall, controllerParameter, parametersParameter);
33 return lambda.Compile();
34 }
35 }

研究过表达式树的同鞋一定知道,在这个方法中构建了一个调用Action方法的表达式树,并通过lambda.Compile()返回调用过程的委托。lambda表达式树调用方法的效率如何,老赵早已在这篇文章[点击学习]分析过,它的效率跟静态调用相差无几的。所以担心“反射”降低性能的朋友大可以放心了。

后记

本文虽然主题不够明确,讲了调试又讲MVC(我就喜欢跟着思维走,呵呵),也反映了些问题。假如我们通过一般的调试手段去分析这个问题,那么我 们得先把MVC源码给分析一边吧,然后呢,我们好容易找到了入口点,Controller的 Execute方法,加了断点了,然后就开始淌水了,趟了好深好深的一趟水,然后执行Action了,然后我们要再次调试,回忆刚才从哪里跳到 Action,终于找到了。好辛苦!而用IntelliTrace只是轻轻点击下鼠标,就完成了任务。另外,选MVC框架源码的作用,说明项目足够复杂 时,这样的调试功能的价值才能体现出来。另外,不是也有个收获么,知道了MVC框架怎么通过地址栏参数映射到方法了。

[转载]Web性能优化实践——应用层性能优化

mikel阅读(1087)

[转载]Web性能优化实践——应用层性能优化 – 木子博客 – 博客园.

随着公司项目的进一步推广,用户数量的增加,已经面临着单台服务器不能负载的问题。

这次的优化由于时间关系主要分两步走,首先优化应用层代码以提高单台服务器的负载和吞吐率。之后再进行分表,引入队列、MemCached等分布式应用。

项目背景:这是一个在线竞赛的项目(http://race.gwy.591up.com),在竞赛的时间段内数据库的写入压力很大。

当前问题:1、服务器带宽压力。2、数据库压力。

下图是Web服务器CPU使用率报表。

总体上应用层服务器的CPU使用率不高。

下图是Web服务器带宽报表。

从这个报表可以看到,每块竞赛带宽的占用都会出现一个很高的峰值。服务器是百兆的带宽,理论上可用的最大流量是12.5M,有些时候已经快接近理论的峰值了。

我们再看数据库服务器的带宽报表。

同样的,数据库服务器同样的在竞赛的时间点流量超大,很明显这种情况是不正常的,查询肯定是有问题的。

面对这样的问题,确定了第一期主要做以下的优化。

1、 用flash storage做用户做题断点记录。(做题断点:类似程序断点,用户做到第N题时退出做答,下次进入时依然定位到第N题。)这里原来是用数据库存储的,但 用户每做一题都会执行一条UPDATE语句,而数据库是MySQL的MyISAM引擎,更新操作经常被堵塞。

2、更改系统交卷行为。原来系统在用户做完提交竞赛后,会执行一条UPDATE语句更新用户提交试卷的时间点。同样的这个UPDATE也是在同一时间段内执行,和产品经理沟通后,确认在最后一分钟的时候可以不用再执行这个更新,允许用户的作答时间有1分钟的误差。

3、调整数据库的更新语句为插入语句,这个优化点因为时间问题,推迟到第二个优化阶段再处理。

4、调整应用服务器以支持LVS集群。对当前系统进行分析后,暂时可以不用调整代码直接部署集群,问题是在多台服务器内都会存在相同的进程内缓存,这种情况暂时是可以接受的,后期需要改到MemCached集中管理缓存。

5、 等待成绩页面同一时间跳转的压力问题。在线竞赛的提交时间点很集中,用户做答完题目后,会统一跳转到一个页面等待答案(这时后台的Windows 服务在进行竞赛统计),这里服务器的并发、带宽压力都非常大。因此,优化这里不进行跳转,而是在当前的页面等待,并且会自动给不同的用户分配不同的等待时 间,以避免占满服务器的带宽。

6、 每场完整的公务员考试试卷,题目资源有150K-200K,因为作答和查看解析是在不同的页面,之前的实现会造成题目的多次加载,严重的浪费了带宽资源。 于是这里优化成Handler输入静态资源加载,从服务器加载一次之后,后面所有的地方调用到题目都可以从浏览器的本地缓存中加载带省服务器带宽。同时, 服务器上只对静态资源服务器开启了GZip压缩,对动态文件进行压缩会浪费服务器的CPU资源,而只对Handler输出的题目进行GZip压缩,一方面 节省了CPU,另一方面把150K-200K的题目资源压缩到了50K左右。

7、数据库性能优化。调整了代码中查询的各个条件的位置,使查询语句能够更多的使用到索引。同时把原来每次一条的插入操作修改为一次插入多条等一些数据库查询优化。

任何一个优化都要针对已经存在的问题,从服务器监控的报表可以看到我们这个网站应用服务器带宽压力、数据库服务器带宽压力都很大,应用服务器的CPU使用率不高,因此,主要的优化是对应用服务器带宽和数据库服务器的写入压力做的优化,因为目的很明确,效果也是比较明显的。

文中提到了用Handler来输出静态资源让浏览器缓存,附上这个代码,其它的优化针对性很高,就不再啰嗦了,主要的还是记录下这次优化的工作方式和工作方法。

Handler输出的静态资源使用了.NET流压缩,于是我们声明一个压缩器接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.IO;
namespace ND.Race.Compressor
{
/// <summary>
/// 压缩器接口
/// </summary>
public interface ICompressor
{
string EncodingName { get; }
bool CanHandle(string acceptEncoding);
void Compress(string content, Stream targetStream);
}
}

GZip压缩器实现这个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System.IO;
using System.IO.Compression;
using System.Text;
namespace ND.Race.Compressor
{
public sealed class GZipCompressor : ICompressor
{
public string EncodingName
{
get { return "gzip"; }
}
public bool CanHandle(string acceptEncoding)
{
return !string.IsNullOrEmpty(acceptEncoding) &&
acceptEncoding.Contains("gzip");
}
public void Compress(string content, Stream targetStream)
{
using (var writer = new GZipStream(targetStream, CompressionMode.Compress))
{
var bytes = Encoding.UTF8.GetBytes(content);
writer.Write(bytes, 0, bytes.Length);
}
}
}
}

同样的Deflate压缩器也实现这个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System.IO;
using System.IO.Compression;
using System.Text;
namespace ND.Race.Compressor
{
public sealed class DeflateCompressor : ICompressor
{
public string EncodingName
{
get { return "deflate"; }
}
public bool CanHandle(string acceptEncoding)
{
return !string.IsNullOrEmpty(acceptEncoding) &&
acceptEncoding.Contains("deflate");
}
public void Compress(string content, Stream targetStream)
{
using (var writer = new DeflateStream(targetStream, CompressionMode.Compress))
{
var bytes = Encoding.UTF8.GetBytes(content);
writer.Write(bytes, 0, bytes.Length);
}
}
}
}

如果浏览器不支持流压缩,那我们只能直接输出内容了,因此我们还需要一个不进行压缩的处理类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System.IO;
using System.Text;
namespace ND.Race.Compressor
{
public sealed class NullCompressor : ICompressor
{
public string EncodingName
{
get { return "utf-8"; }
}
public bool CanHandle(string acceptEncoding)
{
return true;
}
public void Compress(string content, Stream targetStream)
{
using (targetStream)
{
var bytes = Encoding.UTF8.GetBytes(content);
targetStream.Write(bytes, 0, bytes.Length);
}
}
}
}

现在我们就可以开始编码我们的Handler了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
public class QuestionCacheHandler : IHttpHandler
{
#region 静态变量
/// <summary>
/// 资源过期时间
/// </summary>
private static readonly int durationInDays = 30;
/// <summary>
/// 流压缩接口
/// </summary>
private static readonly ICompressor[] Compressors = {
new GZipCompressor(),
new DeflateCompressor(),
new NullCompressor()
};
#endregion
#region 私有变量
/// <summary>
/// 内存流压缩类
/// </summary>
private ICompressor compressor;
/// <summary>
/// ETAG
/// </summary>
private string eTagCacheKey;
/// <summary>
/// 竞赛场次Id
/// </summary>
private long raceId;
#endregion
public void ProcessRequest(HttpContext context)
{
if (context == null) return;
long.TryParse(context.Request.QueryString["raceId"], out raceId);
if (raceId == 0) return;
var acceptEncoding = context.Request.Headers["Accept-Encoding"];
compressor = Compressors.First(o => o.CanHandle(acceptEncoding));
eTagCacheKey = string.Concat(raceId, "/etag");
if (IsInBrowserCache(context, eTagCacheKey)) return;
SendOutputToClient(context, true, eTagCacheKey);
}
/// <summary>
/// 发送内容到客户端
/// </summary>
/// <param name="context"></param>
/// <param name="insertCacheHeaders"></param>
/// <param name="etag"></param>
private void SendOutputToClient(HttpContext context, bool insertCacheHeaders, string etag)
{
string content = "";
MemoryStream memoryStream = new MemoryStream();
compressor.Compress(content, memoryStream);
byte[] bytes = memoryStream.ToArray();
HttpResponse response = context.Response;
if (insertCacheHeaders)
{
HttpCachePolicy cache = context.Response.Cache;
cache.SetETag(etag);
cache.SetOmitVaryStar(true);
cache.SetMaxAge(TimeSpan.FromDays(durationInDays));
cache.SetLastModified(DateTime.Now);
cache.SetExpires(DateTime.Now.AddDays(durationInDays)); // HTTP 1.0 的浏览器使用过期时间
cache.SetValidUntilExpires(true);
cache.SetCacheability(HttpCacheability.Public);
cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
cache.VaryByHeaders["Accept-Encoding"] = true;
}
response.AppendHeader("Content-Length", bytes.Length.ToString(System.Globalization.CultureInfo.InvariantCulture));
response.ContentType = "text/plain";
response.ContentType = "application/x-JavaScript";
response.AppendHeader("Content-Encoding", compressor.EncodingName);
if (bytes.Length > 0)
response.OutputStream.Write(bytes, 0, bytes.Length);
if (response.IsClientConnected)
response.Flush();
}
/// <summary>
/// 是否浏览器已经缓存
/// </summary>
/// <param name="context"></param>
/// <param name="etag"></param>
/// <returns></returns>
private bool IsInBrowserCache(HttpContext context, string etag)
{
string incomingEtag = context.Request.Headers["If-None-Match"];
if (String.Equals(incomingEtag, etag, StringComparison.Ordinal))
{
context.Response.Cache.SetETag(etag);
context.Response.AppendHeader("Content-Length", "0");
context.Response.StatusCode = (int)System.Net.HttpStatusCode.NotModified;
context.Response.End();
return true;
}
return false;
}
public bool IsReusable
{
get
{
return false;
}
}
}

服务器端代码通过Http请求Header的Accept-Encoding来判断是否支持流压缩,再通过Header的etag来判断浏览器中是否已经有缓存副本。

关注更多相关内容,请移步:  http://blog.moozi.net/

[转载]Google Local Search API 简介

mikel阅读(1281)

[转载]Google Local Search API 简介 – liongis – 博客园.

Google 提供了一个基于JavaScript的本地搜索的API,我们可以通过这个API来嵌入到我们的应用程序中,实现搜索的功能。如javascrtip,Flash,java等。

此接口返回的数据为JSON格式的数据,可以方便进行解析。

Google Local Search API首页地址是:

http://code.google.com/intl/zh-CN/apis/maps/documentation/localsearch/index.html

以下是一个简单的例子:

1 <DOCTYPE html> 2  <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="content-type" content="text/html; charset=utf-8"/> 5 <title>Google Search API Sample</title> 6 <script src="http://www.google.com/jsapi?key=INSERT-YOUR-KEY"></script> 7 <script type="text/javascript"> 8 // This code generates a "Raw Searcher" to handle search queries. The Raw Searcher requires 9   // you to handle and draw the search results manually. 10   google.load('search', '1'); 11 12 var localSearch; 13 function searchComplete() { 14 15 // Check that we got results 16 document.getElementById('content').innerHTML = ''; 17 if (localSearch.results && localSearch.results.length > 0) { 18 for (var i = 0; i < localSearch.results.length; i++) { 19 20 // Create HTML elements for search results 21 var p = document.createElement('p'); 22 var a = document.createElement('a'); 23 var b = document.createElement('b'); 24 var c = document.createElement('c'); 25 a.href = localSearch.results[i].url; 26 a.innerHTML = localSearch.results[i].title; 27 b.innerHTML = "<br>" + 28 localSearch.results[i].streetAddress; 29 c.innerHTML = "<br>" + 30 localSearch.results[i].city + "," + 31 localSearch.results[i].region; 32 33 // Append search results to the HTML nodes 34 p.appendChild(a); 35 p.appendChild(b); 36 p.appendChild(c); 37 document.body.appendChild(p); 38 } 39 } 40 } 41 42 function onLoad() { 43 44 // Create a LocalSearch instance. 45 localSearch = new google.search.LocalSearch(); 46 47 // Set the Local Search center point 48 localSearch.setCenterPoint("New York, NY"); 49 50 // Set searchComplete as the callback function when a search is complete. The 51 // localSearch object will have results in it. 52 localSearch.setSearchCompleteCallback(this, searchComplete, null); 53 54 // Specify search quer(ies) 55 localSearch.execute('coffee New York NY'); 56 57 // Include the required Google branding. 58 // Note that getBranding is called on google.search.Search 59 google.search.Search.getBranding('branding'); 60 } 61 62 // Set a callback to call your code when the page loads 63 google.setOnLoadCallback(onLoad); 64 65 </script> 66 </head> 67 <body style="font-family: Arial;border: 0 none;"> 68 <div id="branding" style="float: left;"></div><br /> 69 <div id="content">Loading...</div> 70 </body> 71 </html>

其中最重要的是调用这个地址:

http://ajax.googleapis.com/ajax/services/search/local?v=1.0&q=Palm%20Springs%20CA
两个必须的参数如下:
v:版本号,如1.0
q:搜索的关键字
还有一些其它常可以用到的参数:
key:搜索的时候,需要验证的key值,这个你必须到google上去申请
sll:中心坐标,你可以指定一个坐标为中心进行搜索
rsz:每页显示几条数据,值为1-8,当然,每次搜索最大记录数为64

我们来看看常见的几种语言是如何来使用的:
使用Flash
var service:HTTPService = new HTTPService(); service.url = 'http://ajax.googleapis.com/ajax/services/search/local'; service.request.v = '1.0'; service.request.q = 'Palm%20Springs%20CA'; service.request.key = 'INSERT-YOUR-KEY'; service.resultFormat = 'text'; service.addEventListener(ResultEvent.RESULT, onServerResponse); service.send(); private function onServerResponse(event:ResultEvent):void { try { var json:Object = JSON.decode(event.result as String); // now have some fun with the results... } catch(ignored:Error) { } }
使用Java

URL url = new URL("http://ajax.googleapis.com/ajax/services/search/local?" + "v=1.0&q=barack%20obama&key=INSERT-YOUR-KEY&userip=INSERT-USER-IP"); URLConnection connection = url.openConnection(); connection.addRequestProperty("Referer", /* Enter the URL of your site here */); String line; StringBuilder builder = new StringBuilder(); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); while((line = reader.readLine()) != null) { builder.append(line); } JSONObject json = new JSONObject(builder.toString()); // now have some fun with the results...

使用PHP

$url = "http://ajax.googleapis.com/ajax/services/search/local?" + "v=1.0&q=barack%20obama&key=INSERT-YOUR-KEY&userip=INSERT-USER-IP"; // sendRequest // note how referer is set manually $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_REFERER, /* Enter the URL of your site here */); $body = curl_exec($ch); curl_close($ch); // now, process the JSON string $json = json_decode($body); // now have some fun with the results...


今天先介绍到这里,以后我会更详细的进行简介一下。

下面是我用Flex做的一个示例,结合了Google Map,支持关键字搜索,并可以导出结果。

再次申明,程序只用于学习使用,请必用于商业。需要安装Flash AIR

下载地址是:http://files.cnblogs.com/liongis/GMapLocalSearch.rar

[转载]html5声频audio和视频video

mikel阅读(1168)

[转载]html5声频audio和视频video – 破狼 – 博客园.

html5作为下一代web标准,年前轩起了html5热潮。对于html5我只是本着了解看看。关于html5和 RIA(silverlight,flash,JavaFx等)我不想说什么,也没有什么可说的,存在就有其存在的理由。孰优孰劣,留给事实、时间来证明 的。

在html5中出现了一些新特性:

  • canvas 元素
  • 视频 video 和 声频audio 元素 ;
  • 对本地离线存储(localStorage,sessionStorage)的支持 ;
  • 新增特殊内容元素:article、footer、header、nav、section ;
  • 新增表单控件: calendar、date、time、email、url、search 。

今天看看视频和声频:在html5中规定了视频的标准方法:video

<video src="xxx.ogg" controls="controls">你的浏览器还不支持哦</video>
  我们也可以设置多个source,浏览器会为我们选择第一个可识别的视频来播放,形如:
<video width="320" height="240" controls="controls">
<source src="xxx.ogg" type="video/ogg">
 <source src="xx1.mp4" type="video/mp4">
你的浏览器还不支持哦</video>
video属性有:
属性 描述
autoplay autoplay 如果出现该属性,则视频在就绪后马上播放。
controls controls 如果出现该属性,则向用户显示控件,比如播放按钮。
height 像素 设置视频播放器的高度。
loop loop 如果出现该属性,则当媒介文件完成播放后再次开始播放。
preload preload 如果出现该属性,则视频在页面加载时进行加载,并预备播放。

如果使用 “autoplay”,则忽略该属性。

src url视频地址 要播放的视频的 URL。
width 像素 设置视频播放器的宽度。
autobuffer Autobuffer

(自动缓冲)

在网页显示时,该二进制属性表示是由用户代理(浏览器)自动缓冲的内容,还是由用户使用相关API进行内容缓冲
poster url图片地址

当视频未响应或缓冲不足时,该属性值链接到一个图像。该图像将以一定比例被显示出来

在HTML5 规定了声频标准为 audio 元素,audio 元素能够播放声音文件或者音频流。

audio格式和video相似:直接行多source:

<audio controls="controls">
  <source src="xx.ogg" type="audio/ogg">
  <source src="xx1.mp3" type="audio/mpeg">
你的浏览器还不支持哦
</audio>
其属性比video少了height、width、poster。
在我们的开发中多媒体越来越重要,html5出现了这些video和audio。

[转载]介绍一个开源的ORM--ORM.NET

mikel阅读(816)

[转载]介绍一个开源的ORM–ORM.NET – Code,I hate you… – 博客园.

一.介绍

ORM.NET是一个开源的.net对象关系映射的代码生成工具,它根据数据库结构生成一个数据实体层,为每一个表生成一个对应的类,为表的每一个字段生成类的一个属性。

通过生成的DataManager对象,不用使用存储过程和嵌入SQL脚本就可以很容易根据复杂的条件检索数据,除此之外,数据更新,插入和删除可以通过一次调用就可以对数据进行保存。

官网地址:http://orm-net.sourceforge.net/

下载:http://sourceforge.net/projects/orm-net/files/ORM.NET/

二.演示

http://orm-net.sourceforge.net/tour.html

三.官方提供的使用示例

//根据数据库连接字符串创建一个数据管理对象

DataManager data = new DataManager(Config.Dsn);

//获取一个Customers集合并检索关联的Order表

CustomersCollection customers = data.GetCustomersCollection(FetchPath.Customers.Orders);

//根据ContactName进行降序排序

customers.SortByContactName(SortDirection.Descending);

//更新Customers表中CustomerID=”ALFKI”的记录,把ContactName修改为”Peach”

customers.FindByCusmerID(“ALFKI”).ContactName = “Peach”;

//创建一个新的customer对象

Customers customer = data.NewCustomers(“Tes7″,”Orero software”);

customer.ContactName = “Tech Support”;

//创建新的关联对象

Orders order = customer.NewOrders();

order.OrderDate = DateTime.Now;

//通知DataManager更新数据库一直持续到所有的操作完成

data.CommitAll();

说明:我只是翻译过来跟大家分享一下这个ORM,让大家多了解一下,具体我没在项目中用过也没测试过,个人觉得这样的ORM只适合做网站和小项目可能提高开发效率。

[转载]AndroidNDK开发之“文件操作”

mikel阅读(1013)

[转载]AndroidNDK开发之“文件操作” – duicky – 博客园.

其实和上层没什么关系,主要是通过C来完成文件的基本操作。不好意思大家,时间不够,不多说,贴上关键代码。

关键文件代码:

MainActivity.java

package com.scan.file;

import Android.app.Activity;
import Android.os.Bundle;
import Android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

private static final String TAG = “File”;
private Button doc = null;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

initControls();
}

/**
* 初始化控件
*/
public void initControls() {
doc = (Button) this.findViewById(R.id.btn_do_c);
doc.setOnClickListener(new MyButtonOnClickListener());
}

/**
* 监听ButtonOnClick
*
* @author lxf
*
*/
class MyButtonOnClickListener implements OnClickListener {

@Override
public void onClick(View v) {
switch (v.getId()) {

case R.id.btn_do_c:
String do_c_result = doCMethod();
displayMessage(do_c_result);
break;
}

}

}

/*
* Toast显示消息
*/
private void displayMessage(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}

/**
* 测试方法
*
* @return
*/
public native String sayHello();

/**
* 执行C底层的方法
*
* @return
*/
public native String doCMethod();

/*
* 载人本地库文件
*/
static {
System.loadLibrary(“AndroidJni”);
}
}

file.h

#include <string.h>
#include <stdio.h>
#include “define.h”

unsigned short File_Open(FILE_HANDLE** FileHandle, char* name, unsigned short flag,unsigned short mode);

unsigned short File_Close(FILE_HANDLE* FileHandle);

unsigned short File_GetSize(FILE_HANDLE* FileHandle, unsigned long* FileSize);

unsigned short File_Read(FILE_HANDLE* FileHandle, char* buf, unsigned long count,unsigned long* ReadCount);

unsigned short File_Write(FILE_HANDLE* FileHandle, char* buf, unsigned long count,unsigned long* WriteCount);

unsigned short File_Seek(FILE_HANDLE* FileHandle, long offset, short origin,unsigned long* SeekLen);

unsigned short File_Delete(char* name);

unsigned short File_ISExist(char* path);

unsigned short File_Create_Dir(char* dirName);

unsigned short File_Delete_Dir(char* dirName);

Android.mk
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := AndroidJni
LOCAL_SRC_FILES := AndroidJni.c File.c SyncmlEngine.c

LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

AndroidJni.c

#include <string.h>
#include <jni.h>
#include <android/log.h>
#include “SyncmlEngine.h”

//测试方法sayHello
jstring Java_com_scan_file_MainActivity_sayHello(JNIEnv* env, jobject thiz) {
//打印信息出来
__android_log_print(ANDROID_LOG_INFO, “JNIMsg”, “SayHello”);
return (*env)->NewStringUTF(env, “Hello from JNI ! sayHello”);
}

//执行C底层方法
jstring Java_com_scan_file_MainActivity_doCMethod(JNIEnv* env, jobject thiz) {
//打印信息出来
__android_log_print(ANDROID_LOG_INFO, “JNIMsg”, “doCMethod”);

SyncmlStart();

return (*env)->NewStringUTF(env, “Do C Method OK!”);
}

file.c

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <android/log.h>
#include “File.h”

/**
* File_Open–打开文件
* flag–读\写\读写\追加
* mode–如果没有,是否新建
*/
unsigned short File_Open(FILE_HANDLE** FileHandle, char* name,
unsigned short flag, unsigned short mode) {

if (FileHandle == NULL) {
return 100;
}
if (name == NULL) {
return 100;
}
char type[128] = “”;

if (flag == FO_OREAD) {
if (mode != FO_CREATE) {
strcpy(type, “r”); //只读,不建文件
} else {
strcpy(type, “r+”);
}
} else if (flag == FO_OWRITE) {
if (mode != FO_CREATE) {
strcpy(type, “w”); //只写,不建文件
} else {
strcpy(type, “w+”);
}
} else if (flag == FO_RW) {
if (mode != FO_CREATE) {
strcpy(type, “a”);
} else {
strcpy(type, “a+”);
}
} else if (flag == FO_APPEND) {
if (mode != FO_CREATE) {
strcpy(type, “a”);
} else {
strcpy(type, “a+”);
}
}

*FileHandle = fopen(name, type);
return 0;
}

/**
* File_Close–关闭文件
* 返回值
* 0–关闭成功;否则失败
*/
unsigned short File_Close(FILE_HANDLE* FileHandle) {
if (FileHandle == NULL) {
return 100;
}
return fclose(FileHandle);
}

/**
* File_GetSize–得到文件长度
* FileSize–返回文件长度
*/
unsigned short File_GetSize(FILE_HANDLE* FileHandle, unsigned long* FileSize) {
if (FileHandle == NULL) {
return 100;
}
fseek(FileHandle, 0L, SEEK_END);
*FileSize = ftell(FileHandle);
return 0;
}

/**
* File_Read–读取文件到buf
* count–读取的长度
* ReadCount–返回已读取的长度
*/
unsigned short File_Read(FILE_HANDLE* FileHandle, char* buf,
unsigned long count, unsigned long* ReadCount) {
if (FileHandle == NULL) {
return 100;
}
*ReadCount = fread(buf, 1, count, FileHandle);
__android_log_print(ANDROID_LOG_INFO, “JNIMsg”,
“File_Read           ReadCount=%d”, *ReadCount);
return 0;
}

/**
* File_Write–从buf中写入文件
* count–写入的长度
* WriteCount–返回已写入的长度
*/
unsigned short File_Write(FILE_HANDLE* FileHandle, char* buf,
unsigned long count, unsigned long* WriteCount) {
if (FileHandle == NULL) {
return 100;
}
unsigned short write_result = fwrite(buf, count, 1, FileHandle); // 返回值是成功写入的项目数
if(write_result == 1) {
*WriteCount = write_result * count;
}
return write_result;
}

/**
* File_Seek–给文件偏移量
* offset–偏移量
* origin–偏移方向
* SeekLen–返回已偏移的长度
* 返回值0–成功,其他失败
*/
unsigned short File_Seek(FILE_HANDLE* FileHandle, long offset, short origin,
unsigned long* SeekLen) {
unsigned short seek_result = fseek(FileHandle, offset, origin);
if(seek_result == 0) {
*SeekLen = offset;
}
return seek_result;
}

/**
* File_Delete–删除文件
* 0–删除成功  -1–删除失败
*/
unsigned short File_Delete(char* name) {
if (name == NULL) {
return 100;
}
return remove(name);
}

/**
* File_ISExist–判断文件是否存在
* 0存在  -1不存在
*/
unsigned short File_ISExist(char* path) {
if (path == NULL) {
return 100;
}
return access(path, 0);
}

/**
* File_Create_Dir–创建文件目录
* 返回0–成功
*/
unsigned short File_Create_Dir(char* dirName) {
if (dirName == NULL) {
return 100;
}
return mkdir(dirName, S_IRWXU);
}

/**
* File_Delete_Dir–删除文件目录
*/
unsigned short File_Delete_Dir(char* dirName) {
if (dirName == NULL) {
return 100;
}
DIR* dp = NULL;
DIR* dpin = NULL;
char *pathname = (char*) malloc(256);
memset(pathname, 0, 256);
struct dirent* dirp;
dp = opendir(dirName);
if (dp == NULL) {
__android_log_print(ANDROID_LOG_INFO, “JNIMsg”,
“File_Delete_Dir      your input directory is not exist!”);
return 100;
}
while ((dirp = readdir(dp)) != NULL) {
if (strcmp(dirp->d_name, “..”) == 0 || strcmp(dirp->d_name, “.”) == 0)
continue;
strcpy(pathname, dirName);
strcat(pathname, “/”);
strcat(pathname, dirp->d_name);
dpin = opendir(pathname);
if (dpin != NULL) {
closedir(dpin);
dpin = NULL;
File_Delete_Dir(pathname);
} else {
remove(pathname);
}
}
rmdir(dirName);
closedir(dp);
free(pathname);
pathname = NULL;
dirp = NULL;

return 0;
}

有看不懂的地方可以留言,一起探讨。