[转载]无名良品-首页优化(Flash篇)

mikel阅读(1036)

[转载]无名良品-首页优化(Flash篇) – zaohe – 博客园.

Flash的首屏应用

简介:

良品首页3.0版本在设计的时候,想在首屏添加一些特殊的效果,例如彩蛋之类,所以整个首屏使用了Flash,在高品质的显示效果和动画效果背后带来了大量问题,下面来一一分析。

问题:

1. 版面大,使用了1000 * 500的分辨率,分2屏展示。

2. 高品质的图片,带宽需求高。

3. 灵活的展示方式,图片个数不确定,请求数目多,平均每次版本 10张图片左右。

4. 图片分布在CDN的不同服务器上,加上FlashSWF文件,图片的配置文件,Flash的资源一共分布在6个服务器上,cross-domain 验证开销巨大。

5. 丢失Ref问题

6. 某些用户设备无对Flash的支持

各个版本以及改进:

1. 上线前的版本:

图片全部都是PNG24,所有图片(2屏)同时加载,首次打开页面,所有的请求数在34个左右,图片大小在1.1M左右。一般网速下打开首屏需要 10-15秒。

2. 正式上线的版本:

与上线前得版本相比,打开页面时,仅加载第一屏的图片,第二屏图片延迟加载,首屏的请求数下降3-4个,图片大小降低到800K 左右。打开首屏8-10秒。

此时问题依然严重,所有人都反映首屏加载慢,我们说服了设计师,将首屏的PNG替换成JPG,画质在85以上,由设计师自由调整,保证画质的前提下,大大降低图片大小。

3. 优化图片后的版本:

加载图片的大小降低一半,请求数未变,首屏图片降低到400K左右,打开时间在3-5秒。

到了这个时候,图片的大小已经没法优化,Flash在未加载完时,首屏一片空白,至少3秒才能看到图片加载,网速慢的情况下5-10秒钟都是空白,仔细分析发现时间有很大部分消耗在Flashcross-domain验证上,在验证时,会阻塞对此服务器的请求。

此时需要解决的问题是,将空白去掉,加载完成Flash前显示首屏的图片,等Flash加载完成后,替换掉Dom元素。

4. 加载Flash前显示首屏图片

部分图片重复加载,整个首屏的请求数上升8个左右,图片的请求大小上升到900K,但是由于图片在Flash加载前已经加载,Flash加载的图片仅是在缓存中加载,所以,图片整体大小并未增加,但是缓存机制无法保证在所有用户都起作用。

设计师对此次改进依然不满意,因为有2-3秒内首屏没有动画效果,所以一个Flash首屏的JS提单版本开发出来,在整个Flash加载以及内部的图片加载完成后隐藏JS版生成的Dom元素,隐藏而不是移除的原因是为了后台的统计。

5. JS部分替代版本:

所有首屏图片重复加载,对首屏的影响跟前面的版本相比差别不大,增强了用户体验,在不支持Flash的浏览器中依然可以使用。

Flash 版本从上线到这个时候已经半个月,无论前端还是BI部门对这个首屏都不满,特别是BI部门,后台系统对点击数据的统计是基于 a 标签的,Flash版本使BI部门只能手工统计,而且不准确。我们找了设计师后,确认一些额外的超炫功能暂时不再上线,我们就使用完全的JS版本替代Flash版本。

6. JS完全替代版本:

首页,首屏的请求数降低到25个,首屏资源的大小下降到370K左右,加载时间2-3秒,中间没有白屏等待Flash加载的现象。

对比表格

版本 请求数 首屏资源大小 首屏完全加载完成后资源大小 加载时间 加载完成前显示空白时间
上线前 34 1.1M 1.1M 10-15s 2-5s
上线后 30 900K 1.1M 8-10s 2-5s
优化图片后 30 400k 700K 5-8s 2-5s
预加载Flash图片 40 700K 1.1M 5-8s 无(2秒内可以显示图片)
JS部分替代 44 700K 1.5M 5-8s 无(2秒内可以显示图片)
JS完全替代 25 370k 520K 2-3S

*首屏的加载时分屏加载的,第一屏切到第二屏前加载第二屏的图片。

*加载时间是按首次加载算的,浏览器未缓存的情形

总结:

Flash在高访问量的首页应用有一定的限制,特别是作为展示多个图片的容器,因为这种网站一般使用CDN服务器,所以图片会分布在多个服务器上,由此带来的cross-domain问题很难解决,其他的诸如丢失Ref等问题网络上都有现成的解决方案(模拟Form提交)。

[转载]SQL常用技术总结

mikel阅读(891)

[转载]SQL常用技术总结 – Wendy ‘s – 博客园.

SQL语句是我们在书写数据存储,查询时必具备的知识,以下把自己个人觉得可用的知识点总计如下:

1、对于存在自动编号的Insert插入语句,返回当前插入行的自动编号ID

eg: Insert into User(uId,Uname,Sex,Email) values(@uId,@UName,@sex,@email)

select @@identity

这样就会返回当前插入语句对应自动编号ID值

2、书写SQL存储过程事务, 在存储过程中你可以执行很多复杂的操作,带事务的存储过程,这样就会保持数据的一致性,要么都执行

成功,在中途出现的失败,系统会自动回滚。

Create proc MyProcTest

(

@UId varchar(10),

@UName varchar(30),

@sex bit,

@email varchar(30)

)

as

begin transaction

Insert into User(uId,Uname,Sex,Email) values(@uId,@UName,@sex,@email)

if @@error=0

commit

else

rollback

end

3、复杂存储过程书写,此翻页存储过程是SQL2000的,当然后继2005版本,出现一些新特性,翻页过程可以简化,如下就把2000下书写的

程序才出来共享

ALTER PROCEDURE GetDataByPage

(

@PageNum INT, –页码

@PageSize INT –每页的数据数

)

AS

DECLARE @totalNum int

SET @totalNum=(SELECT COUNT(*) FROM lqk15)

DECLARE @totalPage INT–获得总页数

SET @totalPage=CEILING(@totalNum*1.0/@PageSize)

IF (@PageNum>@totalPage)

set @PageNum=@totalPage–设置起始号,和结束号

DECLARE @startIndex INT

DECLARE @endIndex   INT
SET @startIndex =(@PageNum-1)*@PageSize

SET @endIndex = @PageNum*@PageSize-1

CREATE TABLE #Temp (classid VARCHAR(14),IdIndex  INT IDENTITY (1,1))
INSERT INTO #Temp SELECT ksh FROM lqk15 ORDER BY ksh
SELECT ksh,xm FROM lqk15 inner join #Temp ON lqk15.ksh=#Temp.classid WHERE IdIndex>=@startIndex and IdIndex<=@endIndex ORDER BY ksh
DROP TABLE #Temp

通过此代码可以对负责SQL语句,临时表的书写有一定的了解和掌握。看明白了,理解的就深入了。

4、查询SQL语句格式化

前天遇到这样个需求要在查询的数据集各字段上带上单引号和逗号,然后导出成txt文本,刚开始打算把查询出来的结果存入记事本后,然后通过正则表达式匹配后进行处理,后来发现SQL就能实现此过程

期望的数据是这样的:

‘03023106’,’80’,’合格’

‘03023113’,’50’,’不合格’

‘03023126’,’88’,’合格’

通过SQL查询语句查询满足此格式:

select ”’+stId+”’,’,”+Score+”’+’,”+Result+”’ from StudentScore

查询出来,直接复制到txt文件中即可。

5、存储过程加密,处于性能和业务逻辑操作考虑,我们往往把一些复杂的SQL语句书写为存储过程,若不想让别人看到存储过程业务逻辑,可以对存储过程进行

加密处理,此过程是不可逆的,所以加密前要对SQL脚本进行一定的备份,每次操作时保证二则同步

加密存储过程语句如下:

Create proc MyProcTest with encryption

as

select * from Exam_Users

执行完成后,查看存储过程,则此存储过程上加了一把小锁,修改按钮也不可操作

如果要修改此存储过程,只能重新执行 Alert proc MyProcTest去覆盖之前的存储 过程。

6、SQL语句实现数据库本地的备份

Backup database Users to Disk ‘D:\\News20110526.bak’

此SQL语句即可实现简单的把users数据库备份在本地的D盘根目录下,名字为News20110526.bak

此过程有两个注意点:

1)、操作可能在客户机,而备份的文件,在SQL数据库服务器上

2)、如果想实现备份文件在当前机器上,则需要额外的操作,与之相关的数据库还原也一样,如果文件在本地想还原数据库到服务器上,单单执行

类似如上语句,则会失败报错。

先写这么多了,一如既往的关注博客园的相关内容。

[转载]分享50个优秀的电子商务网站设计案例

mikel阅读(1562)

[转载]分享50个优秀的电子商务网站设计案例 – 梦想天空(山边小溪) – 博客园.

对于电子商务网站来说,也许销售更多产品比漂亮的外观设计更重要,不过漂亮的东西总是能给用户留下深刻的印象。今天,这篇文章与大家分享50个优秀的电子商务网站设计作品案例,希望能带给你灵感。

Free People

Free People

Me & Mommy-to-be

Me & Mommy-to-be

Keedo

Keedo

UNIQLO

UNIQLO

CellyShop

CellyShop

Big Brown Box

Big Brown Box

Junstil

Jungstil

Little Catwalk

Little Catwalk

Shoon

Shoon

Bagolitas

Bagolitas

Bridge 55

Bridge55

Shop Curious

Shop Curious

Itself

Itself

La Llevo Puesta

La Llevo Puesta

Morphica

Morphica

Wunderbloc

Wunderbloc

Rock Pillars

Rock Pillars

Tilly Moss

Tilly Moss

Cacties

Cacties

Bad Designer Threads

Bad Designer Threads

La Licious

La Licious

Mozilla Store

Mozilla Store

Habitat Shoes

Habitat Shoes

A Better Tomorrow

A Better Tomorrow

Jungle Crazy

Jungle Crazy

Subnormals

Subnormals

Bored

Bored

Brand Neusense

Brand Neusense

Bras & Honey

Bras and Honey

Lanx

Lanx

Martique

Martique

Oraya

Oraya

Costume Studio

Costume Studio

Galante

Galante

Von Dutch

Von Dutch

Bonjour Mon Coussin

Bonjour Mon Coussin

Red is White

Red is White

Composition

Composition

Wire & Twine

Wire & Twine

Mouse to Minx

Mouse to Minx

Like.com

Like.com

Fugitive Toys

Fugitive Toys

One Horse Shy

One Horse Shy

Cosmicsoda

Cosmicsoda

Threadless

Threadless

evelMerch

Evel

ShoeGuru

ShoeGuru

Urban Originals

Urban Originals

Emptees

Emptees

Mia & Maggie

Mia & Maggie

[转载]iphone开发我的新浪微博客户端-用户登录准备篇(1.1)

mikel阅读(913)

[转载]iphone开发我的新浪微博客户端-用户登录准备篇(1.1) – 水的右边 – 博客园.

首先说一下我这个的实现思路,登录支持多个账号,也就是说可以保存多个微博账号登录的时候选择其中一个登录。多个账号信息保存在SQLite的数据库中, 每一个账号信息就是一条记录, 当用户启动微博客户端的时候去取保存在SQLite数据库中的账号记录信息,然后把这些在界面中以列表的形式展示出来,用户可以点击其中的一个账号进入微 博,如果如果启动微博客户端的时候检查到SQLite数据库中一个账号都没有的时候,程序自动显示用户授权认证页面,本客户端是基于oauth认证实现 的,所以使用新微博账号前需要进行授权认证,一个账号只需要第一次做一下授权认证然后会把user_id、Access Token和Access Secret以及用户名称和头像小图标一起保存到sqlite的数据库中,下次登录的时候可以不在需要输入账号和密码,直接在界面中点击已经选择的用户小 图标进入微博。关于Access Token和Access Secret这个我就不解释了,不懂的可以google一下oauth的知识,其实我前面做Android版本的微博客户端的时候写过一篇比较详细的介绍 过关于oauth的随笔可以翻出来参考一下。

从上面的思路来看用户登录有两种可能性,第一种数据库中尚无任何账号信息;第二种数据库中已经包含了一个或以上的账号信息。这两种情况显示不同的用户界面,两种情况的实现过程看下面的描述。

第一种情况实现过程:1.查询sqlite数据库 –>2.无账号记录显示需要进行授权认证的提示信息界面(上图6) –>3.在上一步界面中点击开始按钮进入用户授权界面–>4.用户输入自己的微博账号和密码点击确定然后关闭用户授权界面(上图7)–>5.程序根据用户的授权获取这个账号的信息保存到sqlite库–>6.显示用户登录选择界面默认账号就是用户刚刚授权的账号

第二种情况实现过程:1.查询sqlite数据库 –>2.已有账号记录获取所有账号信息并且显示用户登录选择界面 –>3.读取上一次登录的账号作为界面的默认选择用户显示(如上图2)–>4.点击添加按钮显示账号添加界面(如上图4)–>5.点击切换按钮显示账号选择列表界面(如下图3)–>6.点击删除按钮显示账号删除确认界面(如下图5)

从上面的实现过程看涉及到的主要知识点:1.sqlite数据库操作(创建数据库、创建数据表、插入数据记录、读取数据记录);2、oauth授权认证 (已经由sdk实现了调用相关方法即可);3、NSUserDefaults存取(用来记录上一次的登录账号)

现在正式开始动手做一一些准备工作:

一、到 http://code.google.com/p/minblog4sina/(关于这个可以参看:iphone开发我的新浪微博客户端-开篇)把我的这个sdk项目源代码checkout到本地,然后用xcode打开这个工程,在这个sdk工程的基础上开发微博客户端应用。

二、设计sqlite数据库用来保存用户账号信息,我是用firefox的一个名为SQLite Manager的插件来创建sqlite数据库,这个使用非常简单就不介绍了自己装起来就知道怎么使用,我用它创建了一个名为weibo.sqlite的 数据库,然后这这个数据库中创建了一个名为loginUser的表用来保存账号信息,loginUser表的,这个表包含5个字段分别用来保存 user_id、账号昵称、Access Token、Access Secret、账号小图标,具体如下图:


三、用xcode打开工程,然后把上一步完成的weibo.sqlite文件添加到工程的Resources文件夹中.

四、在工程中添加类型为Objective-C class名为Sqlite的类用来负责sqlite库的创建、记录的读取、记录的添加等操作,同时在Xcode的左边树Frameworks右键添加名 为libsqlite3.dylib的Framework,这个类库封装了sqlite库的操作。

五、打开Sqlite.h添加如下代码,在这个文件中首先import了sqlite3.h,这个就是Frameworks中的 libsqlite3.dylib类库,然后又import了User.h,这个是MinBlog4Sina的sdk中的用户账号对象类。又声明的4个方 法分别是数据库初始化、获取用户记录列表、添加用户记录、删除用户记录。

#import <Foundation/Foundation.h>
#import <sqlite3.h>
#import "User.h"

@interface Sqlite : NSObject {
sqlite3 *database;
}

-(Sqlite *)init;
-(NSMutableArray *)getUserList;
-(BOOL)addUser:(User *)user;
-(BOOL)delUser:(NSString *)name;

@end

六、打开Sqlite.m添加如下代码,这个文件里实现了上述的几个方法,所谓的数据库创建其实就是把上面第三个步骤中添加到工程的weibo.sqlite文件拷贝到本软件的目录中,在下面的-(Sqlite *)init方法中进行了拷贝操作。还有就是用户记录读取和添加中关于BLOB类型的数据处理,这里存储的是图片数据了,在添加的时候把图片转换成bytes类型,读取的时候把bytes类型转换成图片。

#import "Sqlite.h"

@interface Sqlite(private)

-(void)createDatabaseIfNeeded:(NSString *)filename;
-(NSString *)dataFilePath;
-(BOOL)openDatabase;
-(void)closeDatabase;

@end

@implementation Sqlite
//初始化数据库
-(Sqlite *)init
{
if (self= [super init]) {
[self createDatabaseIfNeeded:@"weibo.sqlite"];
}
return self;
}

-(BOOL)delUser:(NSString *)name
{
BOOL sucess=[self openDatabase];
if (sucess) {
NSString *sql=[[NSString alloc] initWithFormat:@"delete from loginUser where screen_name='%@'",name];
char *errorMsg;
if (sqlite3_exec(database, , NULL, NULL, &errorMsg)!=SQLITE_OK) {
NSLog(@"del User error:%s",errorMsg);
sqlite3_free(errorMsg);
}else {

}
;
}
[self closeDatabase];
return sucess;

}

//添加用户记录
-(BOOL)addUser:(User *)user
{
BOOL sucess=[self openDatabase];
if (sucess) {
NSString *sql=[[NSString alloc] initWithFormat:@"insert into loginUser(user_id,screen_name,key,secret,icon)values(?,?,?,?,?)"];
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, , -1, &statement, nil)==SQLITE_OK)
{

NSString *uid=[NSString stringWithFormat:@"%@",user.userId];
const char *user_idChar=[uid UTF8String];
sqlite3_bind_text(statement, 1, user_idChar, strlen(user_idChar), NULL);

const char *screen_nameChar=[user.screen_name UTF8String];
sqlite3_bind_text(statement, 2, screen_nameChar, strlen(screen_nameChar), NULL);

const char *keyChar=[user.key UTF8String];
sqlite3_bind_text(statement, 3, keyChar, strlen(keyChar), NULL);

const char *secretChar=[user.secret UTF8String];
sqlite3_bind_text(statement, 4, secretChar, strlen(secretChar), NULL);

NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:user.profile_image_url]];
sqlite3_bind_blob(statement, 5, [imageData bytes], [imageData length], NULL);

if(sqlite3_step(statement)==SQLITE_DONE)
{
NSLog(@"done");
}else {
NSLog(@"error");
}
sqlite3_finalize(statement);
}
;
}
[self closeDatabase];
return sucess;
}

//获取用户记录列表
-(NSMutableArray *)getUserList
{
NSMutableArray *list;
BOOL sucess=[self openDatabase];
if (sucess) {
NSString *query=@"select * from loginUser";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)==SQLITE_OK)
{
list=[[[NSMutableArray alloc] init]autorelease];
while (sqlite3_step(statement)==SQLITE_ROW) {
char *user_idChar=(char *)sqlite3_column_text(statement, 0);
char *screen_nameChar=(char *)sqlite3_column_text(statement, 1);
char *keyChar=(char *)sqlite3_column_text(statement, 2);
char *secretChar=(char *)sqlite3_column_text(statement, 3);
int bytes = sqlite3_column_bytes(statement, 4);
const void *value = sqlite3_column_blob(statement, 4);

NSString *user_id=[[NSString alloc] initWithUTF8String:user_idChar];
NSString *screen_name=[[NSString alloc] initWithUTF8String:screen_nameChar];
NSString *key=[[NSString alloc] initWithUTF8String:keyChar];
NSString *secret=[[NSString alloc] initWithUTF8String:secretChar];
UIImage *icon;
if( value != NULL && bytes != 0 ){
NSData *data = [NSData dataWithBytes:value length:bytes];
icon=[UIImage imageWithData:data];
}
User *user=[[User alloc] init];
user.userId=user_id;
user.name=screen_name;
user.key=key;
user.secret=secret;
if (icon) {
user.icon=icon;
}

[list addObject:user];
[user release];
[user_id release];
[screen_name release];
[key release];
[secret release];
}
sqlite3_finalize(statement);
}
}
[self closeDatabase];

return list;
}


//根据是否已经存在数据库决定是否要创建数据库
-(void)createDatabaseIfNeeded:(NSString *)filename
{

NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
NSFileManager *filemanage=[NSFileManager defaultManager];
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory=[paths objectAtIndex:0];
NSString *writableDBPath=[documentsDirectory stringByAppendingPathComponent:filename];
BOOL sucess=[filemanage fileExistsAtPath:writableDBPath];
if (sucess) {
return;
}
NSString *defaultDBPath=[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:filename];
NSError *error;
sucess=[filemanage copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!sucess) {
NSLog(@"create Database rror:%@",[error localizedDescription]);
}
[pool release];
}

//获取数据库文件路径
-(NSString *)dataFilePath
{
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath=[[paths objectAtIndex:0] stringByAppendingPathComponent:@"weibo.sqlite"];
return documentPath;
}

//打开数据库
-(BOOL)openDatabase
{
int returnValue=sqlite3_open([[self dataFilePath] UTF8String], &database);
if (returnValue!=SQLITE_OK) {
sqlite3_close(database);
NSLog(@"open Database error:%@",sqlite3_errmsg(database));
return NO;
}else {
return YES;
}

}

//关闭数据库
-(void)closeDatabase
{
if (sqlite3_close(database)!=SQLITE_OK) {
NSLog(@"close Database error:%@",sqlite3_errmsg(database));
}
}

@end

七、在工程中添加类型为Objective-C class名为Global的类用来提供一些非常常用的公用方法。

八、打开Global.h添加如下代码,声明了2个方法,一个是获取png的图片资源、另外一个是进行时间格式化输出的方法,这2个方法都是被声明成+类型,这样方便使用,这2个方法在后面篇章中经常会用到。

#import <Foundation/Foundation.h>


@interface Global : NSObject

+(UIImage *) pngWithPath:(NSString *)path;
+(NSString *) dateInFormat: (time_t)dateTime format:(NSString*) stringFormat;
@end

九、打开Global.m添加如下代码

#import "Global.h"


@implementation Global

+(UIImage *) pngWithPath:(NSString *)path
{
NSString *fileLocation = [[NSBundle mainBundle] pathForResource:path ofType:@"png"];
NSData *imageData = [NSData dataWithContentsOfFile:fileLocation];
UIImage *img=[UIImage imageWithData:imageData];
return img;
}

+(NSString *) dateInFormat: (time_t)dateTime format:(NSString*) stringFormat
{
char buffer[80];
const char *format = [stringFormat UTF8String];
struct tm * timeinfo;
timeinfo = localtime(&dateTime);
strftime(buffer, 80, format, timeinfo);
return [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
}

@end

到处准备篇的工作就已经算是完成了,主要的编码工作就是sqlite的数据库操作了,在下一篇中我们会在这个基础上开始用户功能的开发。

[转载]如何在SQL Server中使用正则表达式

mikel阅读(933)

[转载]如何在SQL Server中使用正则表达式 – 陈希章 – 博客园.

最近处理客户一个需求,是要在SQL Server中对某个列进行校验,使用正则表达式保证该列必须是一个邮件地址。

我们都知道,SQL Server的T-SQL中默认是没有这样的语法的。找了一些资料,下面这个例子还不错,分享给大家参考

http://www.codeproject.com/KB/database/xp_pcre.aspx

大致步骤是

1.下载他提供的那个压缩包,里面有源代码和安装脚本

2.将DLL复制到SQL Server规定的目录

3.运行INSTALL.sql这个脚本

大致使用的效果如下

SELECT master.dbo.fn_pcre_match('billg@microsoft.com','^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$')

这句脚本的意思是,根据后面的正则表达式(一个email的规则)匹配前面的字符串.如果返回1的话,表示匹配到了,否则返回0.很显然,

image

很显然,我们可以根据这个做法设置某个字段的约束。例如下面这样

image

上述做法是通过扩展存储过程来实现的功能,需要通过C++来编写。

当然,如果SQL Server 是2005或者以后的版本,也可以通过托管代码来实现。关于这个话题的基本概念,你可以参考 http://msdn.microsoft.com/en-us/library/ms254498(v=vs.80).aspx

我写好了一个例子,给大家参考:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections;
using System.Text.RegularExpressions;

public partial class UserDefinedFunctions
{
    /// <summary>
    /// 这是一个进行正则表达式验证的函数.作者:陈希章
    /// </summary>
    /// <param name="ValidOption">选项,0为用户自定义,1为网址,2为邮件地址,3为邮政编码</param>
    /// <param name="ValidString">要验证的字符串</param>
    /// <param name="ValidPatten">用户自定义的正则表达式规则</param>
    /// <returns></returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static bool RegExValidate(int ValidOption, string ValidString, string ValidPatten)
    {
        string strRegExPatten = null;
        switch (ValidOption)
        {
            case 0: { strRegExPatten = ValidPatten; break; }
            case 1: { strRegExPatten = @"^[a-zA-Z0-9\-\.]+\.(com|org|net|mil|edu|COM|ORG|NET|MIL|EDU)$"; break; }
            case 2: { strRegExPatten = @"^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$"; break; }
            case 3: { strRegExPatten = @"^[0-9]{4}\s{0,1}[a-zA-Z]{2}$"; break; }
        }

        if (Regex.IsMatch(ValidString,strRegExPatten))
            return true;
        else
            return false;
    }
};

[转载]快速开发新浪微博的firefox插件(下)

mikel阅读(1034)

[转载]快速开发新浪微博的firefox插件(下) – Fei He – 博客园.

上篇主要讲了讲firefox插件的机制,接着我们来看快速开发一个firefox插件中我面临的第二个问题—-Oauth授权(开始开发的时候只是想着快速开发完成,当然授权这块最快的方案自然就是basic auth,但是新浪微博6月1号以后就不支持basic auth了。)。

Oauth的官网上说是这样描述它的用途:

An open protocol to allow secure API authorization in a simpleand standard method from desktop and web applications.

在Oauth的官网上对Oauth有详尽的描述以及不用语言对于Oauth的实现,对于我的开发我需要了解的其实非常简单.

首先对于Oauth有三个url是必须了解的:

  • Request Token URL
  • User Authorize URL
  • Access Token URL

另外,我们还需要了解针对这些URL相应的一些参数:

  • oauth_consumer_key
  • oauth_consumer_secret
  • oauth_signature_method
  • oauth_signature
  • oauth_timestamp
  • oauth_nonce
  • oauth_version

具体这些URL和参数有什么作用呢?我们结合新浪微博来说。对于开发一个新浪微博的应用,你必须在新浪微博申请一个app key和app key secret, 有了它们我们就可以开始了。新浪的Oauth是这样一个流程:

  1. 第 一次你需要做的是Request Token URL,对于新浪微博就是http://api.t.sina.com.cn/oauth/request_token。接着就是请求的参数了,这个时候 你需要把你申请的app key作为oauth_consumer_key, 而把你的app key secret作为oatu_consumer_secret。oauth_signature_method就是你的加密算法,oauth支持 HMAC-SHA1, RSA-SHA1, PLAINTEXT这三种算法,而新浪微博指定需要HMAC-SHA1,所以对与oauth_signature_method我们这里就是HMAC- SHA1了。oauth_timestamp就是请求的时间戳,这个时间戳的范围是现在1970 00:00:00 GMT的秒数,而且每次请求的时间戳必须大于上次的。oauth_nonce就是随机生成的一个字符串,就是为了防止重复请求。 oauth_version现在只能是1.0。最后我们需要对所有参数使用oauth_signature_method指定的加密算法加密作为 oauth_signature。对于请求的参数,我们有两种绑定方式:Get: 拼URL;Post: 放在Http heads里。 有了URL和参数我们就可以发起我们的请求。当新浪微博接受我们的请求检查合法后,会返回给我们一个未授权的oauth_token和 oauth_token_secret,因为我们还没有获得用户的授权。
  2. 有 了上一步的请求返回的oauth_token和oauth_token_secret,在进行下一步请求之前我们需要根据我们应用的类型决定我们下一步的 策略。如果我们是一个web应用,我们有自己的域名我们就可以选择使用callback_url的方式,但是我要做得是一个firefox插件这种方式显 然不合适,那么我只有选择使用verify PIN的方式了。确定了这个,我们请求User Authorize URL(对于新浪微博就是http://api.t.sina.com.cn/oauth/authorize)。这次的参数和上次的参数还是有一些小小 的差别的:首先,我们这次需要用上次请求返回的oauth_token作为oauth_consumer_key, 使用返回的 oauth_token_secret作为oauth_consumer_secret,oauth_signature_method不 变,oauth_timestamp根据这次请求的时间重新生成,oauth_nonce重新生成,oauth_version不变,而 oauth_signature根据这次的参数生成。在User Authorize过程中,我们要加一个参数callback_url。因为我们采用verify PIN的方式,所以这个参数我可以指定为xml。因为这一步就用户授权,那么我们自然还是要输入我们需要访问的新浪微博账号的用户名和密码,分别作为 userId, passwd。新浪微博接收到我们的User Authorize请求检查合法后,会以xml格式返回给我们一个oauth_verifier,证明我们的应用获得了用户的授权。
  3. 有 了oauth_verifier, 加上我们第一步获得oauth_token和oauth_token_secret,其他参数还是按照前面的规则(这次不需要 oauth_callback)构成我们的参数,我们来请求Access Token URL(对于新浪微博就是http://api.t.sina.com.cn/oauth/access_token)。 这一次新浪微博会返回access_token和access_token_secret, 我们授权就通过了。而这个access_token和access_token_secret当我们以后请求新浪微博API时,会作为 oauth_consumer_key和oauth_consumer_secret出现。比如我们要发送微博,新浪微博发送微博的API是 http://api.t.sina.com.cn/statuses/update.json。这时我们还是按照前面的规则组织参数(以 access_token作为oauth_consumer_key, access_token_secret作为oauth_consumer_secret),同时加上参数status,值为我们需要发送的内容就OK 了。

到这一步,我们还不算完,因为我们需要在 firefox的插件中调用新浪微博的oauth。我们都知道JavaScript直接调用新浪微博的API是跨域请求,这是不被允许的。而我们通常解决 跨域请求的方式主要就是:Jsonp, flash,iframe。这里面我们唯一可以尝试是jsonp,而jsonp是需要server端也就是新浪微 博配合的,而实际上新浪微博是不支持jsonp的。那是不是我们没有办法了呢?

@mozilla.org/xmlextras/xmlhttprequest;1

查看了MDN的文档,发现firefox自己的Xmlhttprequest可以,而且可以指定同步还是移步,然后在callback方法里解析返回参数。具体如何实现MDN。

ps:这个插件的代码是作为一个其他应用的一部分,这涉及其他人的劳动,有兴趣可以发站内信或邮件我会单独发一份(只包含firefox插件部分)给你。

[转载]MongoDB on Asp.Net MVC3

mikel阅读(1100)

[转载]MongoDB on Asp.Net MVC3 – Pandora – 博客园.

晚上闲来无事,研究了一下最近很火的MongoDB。感受只有一个:“自由”。

闲话略过,先让MongoDB在Win7 64下跑起来:

1. MongoDB主页

http://www.mongodb.org/

2. 下载

http://downloads.mongodb.org/win32/mongodb-win32-x86_64-1.8.1.zip

3. 解压

e.g. H:\mongodb-win32-x86_64-1.8.1

4. 准备目录

e.g. H:\mongo\data –保存数据库文件 H:\mongo\logs –保存log,在此目录下手动创建一个log.txt

5. 安装

Win键,输入cmd,右键-Run as Administrator

cd H:\mongodb-win32-x86_64-1.8.1\bin

H:

mongod –bind_ip 127.0.0.1 –logpath H:\mongo\logs\log.txt –logappend –dbpath H:\mongo\data –directoryperdb –install

net start “MongoDB”

成功。

6. 测试安装

在之前的CMD窗口中输入:mongo

应该能无异常进入query界面

输入一个3+3,应该能得到6

关掉cmd,这玩意儿没用了。

7. 下载.Net Connector

https://github.com/mongodb/mongo-csharp-driver/archives/master

解压

打开CSharpDriverSetup-2010.sln

Build

失败

删除DriverSetup中对CHM的引用

重新编译,成功

右键DriverSetup – Install

下一步下一步下一步。。。

8. 创建ASP.NET MVC3 Empty project

若无,请自行安装MVC3的TOOL

9. Add ConnectionString in web.config




10. Add Controller

Controller with empty read/write actions

11. Access to the DB

private MongoDatabase GetDB()
{
return MongoDatabase.Create(ConfigurationManager.ConnectionStrings["MongoDB"].ConnectionString);

}

12. Create Action

[HttpPost]

public ActionResult Create(FormCollection collection)
{
try
{
var db = GetDB();
foreach (var key in collection.AllKeys)
{
db["testTable"].Insert(new MongoDB.Bson.BsonDocument{
{key, collection[key]}
});
}

return RedirectToAction("Index");
}
catch
{
return View();
}
}

13. Index Action

public ActionResult Index()
{
try
{
var db = GetDB();

var testTable = db["testTable"].FindAll();
var result = new StringBuilder();
foreach (var testData in testTable)
{
foreach (var property in testData.Names)
{
result.AppendFormat("{0}:{1} ", property, testData[property]);
}

result.Append("
");
}

return Content(result.ToString());
}
catch
{
return View();
}
}

14. Create Views under Views/Home/
Create.cshtml:

@{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
<form action="/Home/Create" method="post"> Name:
<input name="name" type="text" />

Age:
<input name="age" type="text" />

Gender:
<input name="gender" type="text" />

Married:
<input name="married" type="text" />

<input type="submit" value="Add" />
</form>

Index.cshtml

@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<a href="Create">Create New</a>

15. Modify Global.asax.cs

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Create", id = UrlParameter.Optional } // Parameter defaults
);
11. Build & Run
image

Press Add

image

All Done. Happy and enjoy.

补上项目下载:

http://files.cnblogs.com/pandora/MvcApplication1.zip

IIS网站日志分析方法概述 怎样变得更简单更有价值

mikel阅读(907)

转载:http://www.admin5.com/article/20110525/346051.shtml

日志在计算机系统中是一个非常广泛的概念,任何程序都有可能输出日志:操作系统内核、各种应用服务器等等。日志的内容、规模和用途也各不相同,很难一概而论。

本文讨论的日志处理方法中的日志,仅指Web日志。其实并没有精确的定义,可能包括但不限于各种前端Web服务器——apache、lighttpd、tomcat等产生的用户访问日志,以及各种Web应用程序自己输出的日志。

在Web日志中,每条日志通常代表着用户的一次访问行为,例如下面就是一条典型的apache日志:

211.87.152.44 – – [18/Mar/2005:12:21:42 +0800] “GET / HTTP/1.1″ 200 899 “http://www.baidu.com/” “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Maxthon)”

从上面这条日志中,我们可以得到很多有用的信息,例如访问者的IP、访问的时间、访问的目标网页、来源的地址以及访问者所使用的客户端的 UserAgent信息等。如果需要更多的信息,则要用其它手段去获取:例如想得到用户屏幕的分辨率,一般需要使用js代码单独发送请求;而如果想得到诸 如用户访问的具体新闻标题等信息,则可能需要Web应用程序在自己的代码里输出。

为什么要分析日志

毫无疑问,Web日志中包含了大量人们——主要是产品分析人员会感兴趣的信息,最简单的,我们可以从中获取网站每类页面的PV值 (PageView,页面访问量)、独立IP数(即去重之后的IP数量)等;稍微复杂一些的,可以计算得出用户所检索的关键词排行榜、用户停留时间最高的 页面等;更复杂的,构建广告点击模型、分析用户行为特征等等。

既然这些数据是如此的有用,那么当然已经有无数现成的工具可以帮助我们来分析它们,例如awstats、Webalizer,都是专门用于统计分析Web服务器日志的免费程序。

另外还有一类产品,它们不分析直接日志,而是通过让用户在页面中嵌入js代码的方式来直接进行数据统计,或者说我们可以认为它是直接让日志输出到了它们的服务器。典型的代表产品——大名鼎鼎的Google Analytics,另外还有国内的cnzz、百度统计等。

很多人可能会说,既然如此,我们为什么还需要自己来分析日志,有必要吗?当然有。我们的用户(产品分析人员)需求是无穷尽的,上面说的这几类工具虽然很好很强大,但显然没办法满足全部的需求。

无论是本地分析的工具,还是在线的分析服务,它们虽然提很丰富的的统计分析功能,可以做一定程度的配置,但是依然很有限的。要进行稍复杂点的分析,或者要做基于日志的数据挖掘,依然需要自己来完成。

另外绝大多数日志分析工具都是只能用于单机的,数据量稍大就没辙了。同时那些提供在线分析的服务对于单个站点通常也都有最大流量的限制——这是很容易理解的,他们也需要考虑服务器的负载。

所以,很多时候还是得靠自己。

怎么进行日志分析

这并不是一个简单的问题。即使我们把“日志”限定为Web日志,依然包含了成千上万种可能的格式和数据,而是“分析”更是难以定义,也许是简单的统计值的计算,也许是复杂的数据挖掘算法。

下面并不打算讨论这些复杂的问题,而只是笼统的讨论如何构建进行日志分析工作的基础。有了这些基础会让基于日志的简单统计分析变得很简单,并让复杂的分析挖掘等变得可行。

少量数据的情况

先考虑最简单的情况,在数据规模比较小的时候,也许是几十MB、几百MB或者几十GB,总之就是在单机处理尚能忍受的时候。一切都很好办,现成 的各种Unix/Linux工具——awk、grep、sort、join等都是日志分析的利器,如果仅仅是想知道某个页面的PV,一个wc+grep就 能搞定。如果有稍复杂的逻辑,那就使用各种脚本语言,尤其是perl,配合伟大的正则表达式,基本就可以解决所有的问题。

例如,我们想从上面提到的apache日志中得到访问量最高前100个IP,实现很简单:

cat logfile | awk ‘{a[$1]++} END {for(b in a) print b”\t”a[b]}’|sort -k2 -r|head -n 100

不过当我们需要频繁去分析日志的时候,上面的做法在一段时间之后可能就会让我们头疼如何进行各种日志文件、用于分析的脚本文件、crontab文件等等的维护,并且可能会存在大量重复的代码来做数据格式的解析和清洗,这个时候也许就需要更合适的东西,比如——数据库。

当然,要使用数据库来进行日志分析还是需要一些代价的,最主要的就是如何将各种异构的日志文件导入的数据库中——这个过程通常称为 ETL(Extraction-Transformation-Loading)。幸好依然有各种现成的开源、免费的工具来帮助我们做这件事情,并且在日 志种类不太多的时候,自己写几个简单的脚本来完成这项工作也并不困难。例如可以将上面的日志去掉不必要的字段,然后导入如下的数据库中:

现在需要考虑一下用什么数据库来存储这些数据。MySQL是一个很经典的开源数据库,它的传统引擎(MyISAM或者InnoDB,行存储)也 许并不非常的适合日志数据的存储,但是在小数据量的时候还是很够用的。而且,在这方面现在已经有了更好的选择,例如开源且免费的Infobright、 Infinidb,都是专门为数据仓库应用而进行了优化的数据引擎,采用列存储,有良好的数据压缩,处理几百GB的数据基本上不是问题。

使用数据库的好处之一就是,伟大的SQL可以帮我们很简单的完成绝大部分的统计分析工作——PV只需要SELECT+COUNT,计算搜索词排 行只需要SELECT+COUNT+GROUP+ORDER+LIMIT。此外,数据库本身的结构化存储模式也让日志数据的管理变的更简单,减少运维代 价。

同样还是上面的那个例子,简单的一个SQL就可以搞定:

SELECT * FROM (SELECT ip, COUNT(*) AS ip_count FROM apache_log GROUP BY ip) a ORDER BY ip_count DESC LIMIT 100

至于性能问题,数据库的索引和各种优化机制通常会让我们的统计分析工作变得更快,并且上面提到的Infobright和Infinidb都专门 为类似SUM、COUNt之类的聚集应用做了优化。当然也不是绝对的会快,例如在数据库中进行LIKE操作,通常会比grep一个文件还要慢很多。

更进一步的,使用基于数据库的存储,可以很容易的进行OLAP(联机分析处理)应用,从日志中挖掘价值会变的更加简单。

更多的数据怎么办

一个好的数据库似乎会让事情变的很简单,但是别忘了前面提到的都是单机数据库。一台单机在存储容量、并发性上毫无疑问都是有很大限制的。而日志 数据的特点之一就是随时间持续增长,并且由于很多分析过程往往需要历史数据。短时间内的增长也许可以通过分库、分表或者数据压缩等来解决,不过很显然并不 是长久之计。

想要彻底解决数据规模增长带来的问题,很自然的会想到使用分布式技术,结合上面的结论,也许使用某个分布式数据库是一个好选择,那么对最终用户就可以完全透明了。这个的确是很理想的情况,不过现实往往是残酷的。

首先,实现比较完美的分布式数据库(受限于CAP原则)是一个非常复杂的问题,因此在这里并不像单机数据库那样,有那么多开源的好东西可以用, 甚至于商用的也并不是太多。当然,也并非绝对,如果有钱,还是可以考虑一下Oracle RAC、Greenplum之类东西。

其次,绝大多数分布式数据库都是NoSQL的,所以想继续用上SQL的那些优点基本上是没指望,取而代之的都是一些简单、难以使用的接口。单从这点看来,使用这些数据库的价值已经降低很多了。

所以,还是先现实一点,先退一步考虑如何解决的超大规模的日志的分析问题,而不是想如何让它变的像在小数据规模时那样简单。单单想做到这点,目前看来并不是太难,并且依然有免费的午餐可以吃。

Hadoop是伟大的Apache基金会下面的一套分布式系统,包括分布式文件系统(HDFS)、MapReduce计算框架、HBase等很多组件——这些基本都是Google的GFS/MapReduce/BigTable的克隆产品。

Hadoop经过数年的发展,目前已经很成熟了,尤其是其中的HDFS和MapReduce计算框架组件。数百台机器的集群已经被证明可以使用,可以承担PB级别的数据。

Hadoop项目中的HBase是一个按列存储的NoSQL分布式数据库,它提供的功能和接口都非常简单,只能进行简单的K-V查询,因此并不 直接适用于大多数日志分析应用。所以一般使用Hadoop来做日志分析,首先还是需要将日志存储在HDFS中,然后再使用它提供的MapReduce API编写日志分析程序。

MapReduce是一种分布式编程模型,并不难学习,但是很显然使用它来处理日志的代价依然远大于单机脚本或者SQL。一个简单的词频统计计算可能都需要上百代码——SQL只需要一行,另外还有复杂的环境准备和启动脚本。

例如同样还是上面的例子,实现就要复杂的多,通常需要两轮MapReduce来完成。首先要在第一轮的mapper中计算部分ip的访问次数之和,并以ip为key输出:

//遍历输入,并聚合结果

foreach(record in input) {

ip = record.ip;

dict[ip]++;

}

//用emit输出,第一个参数为key,用于reduce的分发

foreach( in dict) {

emit(ip, count);

}

然后在第一轮的reduce中就可以得到每个ip完整的计数,可以顺便排个序,并且只保留前100个。

count = 0;

//对于每个key(ip),遍历所有的values(count),并累加

while(input.values.hasNext()) {

count += input.values.next();

}

//插入到大小为100的堆中

heap_insert(input.key, count);

在reduce结束的时候输出:

//输出当前reduce中count最高的100个ip

foreach( in dict) {

emit(ip, count);

}

由于reduce一般会有很多个,所以最后还需要将所有reduce的输出进行合并、再排序,并得到最终的前100个IP以及对应的访问量。

所以,使用Hadoop来做日志分析很显然不是一件简单事情,它带来了很多的额外的学习和运维成本,但是至少,它让超大规模的日志分析变成了可能。

怎样变得更简单

在超大规模的数据上做任何事情都不是一件容易的事情,包括日志分析,但也并不是说分布式的日志分析就一定要去写MapReduce代码,总是可以去做进一步的抽象,在特定的应用下让事情变得更简单。

也许有人会很自然的想到如果能用SQL来操作Hadoop上的数据该有多好。事实上,不仅仅只有你一个人会这么想,很多人都这么想,并且他们实现了这个想法,于是就有了Hive。

Hive现在也是Hadoop项目下面的一个子项目,它可以让我们用SQL的接口来执行MapReduce,甚至提供了JDBC和ODBC的接 口。有了这个之后,Hadoop基本上被包装成一个数据库。当然实际上Hive的SQL最终还是被翻译成了MapReduce代码来执行,因此即使最简单 的SQL可能也要执行好几十秒。幸好在通常的离线日志分析中,这个时间还是可以接受的。更重要的是,对于上面提到的例子,我们又可以用一样的SQL来完成 分析任务了。

当然Hive并不是完全的兼容SQL语法,而且也不能做到完全的对用户屏蔽细节。很多时候为了执行性能的优化,依然需要用户去了解一些MapReduce的基本知识,根据自己的应用模式来设置一些参数,否则我们可能会发现一个查询执行很慢,或者压根执行不出来。

另外,很显然Hive也并不能覆盖所有的需求,所以它依然保留插入原始MapReduce代码的接口,以便扩展。

更多的问题

即使有了Hive这样一个类似于数据库的东西,我们依然还有很多事情需要做。例如时间久了,可能会有越来越多的需要例行执行的SQL,而这些 SQL中,也许有一些是做了重复的事情;也许有一些的执行效率非常低下,一个复杂的SQL就占满了所有的计算资源。这样的系统会变得越来越难以维护的,直 到有一天例行的SQL终于跑不完了。而最终用户往往不会去关心这些事情,他们只关心自己提交的查询是不是能即时得到响应,怎么样才能尽快的拿到结果。

举个简单的例子,如果发现在使用apache_log的所有查询中,几乎没有人用其中的user_agent字段,那么我们完全可以把这个字段去除掉,或者拆分成两张表,以减少多数查询的IO时间,提高执行的效率。

为了系统化的解决这些问题,我们可能需要引入例行任务的调度机制,可能需要去分析所有的SQL来发现哪些是可以合并的、哪些的性能需要优化,使 用的数据表是不是需要做水平或者垂直分表等等。根据实际情况的不同,这时事情可能是人工来完成,也可能是写程序来自动分析并调整。

再者随着日志类型、分析需求的不断增长。用户会越来越多的抱怨很难找到想要的数据在哪份日志里,或者跑的好好的查询因为日志格式的变化而突然不 能用了。另外上面提到的ETL过程也会变得复杂,简单的转换导入脚本很可能已经解决不了问题。这时候可能需要构建一个数据管理系统,或者干脆考虑建立一个 所谓的数据仓库。

总之,随着日志数据量、日志类型、用户数量、分析需求等等的不断增长,越来越多的问题会逐渐浮现出来,日志分析这件事情可能就不再像我们最初想的那么简单,会变得越来越有价值,也越来越有挑战。

[转载]Android在线开发工具 App Inventor

mikel阅读(1024)

[转载]Android在线开发工具 App Inventor – 神舟龙 – 博客园.

最近在博客园看到一篇报道关于一个人好像是别出心裁的用Android程序求婚,最后成功了,在留言部分看到很多园友说:好浪漫,好幸福;文章中提到的一个在线开发程序,叫做app invertor,于是在网上稍微看了一下,下班回到家之后马上下载,起先是用迅雷下载的,结果下载进度一点也不动,于是乎,翻墙用IE9 下载,下载了两次没有成功,原因可能是保存路径有问题,建议先保存到桌面,然后再安装,下载地址如下http://dl.google.com/dl/appinventor/installers/windows/appinventor_setup_installer_v_1_2.exe(建议翻墙);下载好之后安装就可以了,好像官方建议安装过程中的路径不要修改,以防在加载模拟器的时候找不到路径,当然你也可以自己定义路径,不过在使用模拟器的时候需要人工的输入模拟器的路径,以后就不会出现这种问题了,下面就可以进入按Android的在线编辑页面了,请在浏览器的地址栏中输入:http://appinventor.googlelabs.com/ 就可以看到了,先放图···

第一次进入是会出现下面的界面,请点击new,并填写相应的项目名称即可,我填写的是First

进入First项目,界面如下

整个界面简洁,横向的可以分为四个部分,分别为palette(字典意思是调色板,就是工具),viewer(指示器),components(组件),properties(属性);

Palette:为我们所有将要用到的控件

Viewer:为Android模拟器面板,可拖放控件至此

Components:Viewer中的空间都会在此显示其控件名称

Properties:当某个控件处于编辑状态时,显示其属性可编辑

下面建立我自己的第一个Demo

1.修改标题

2.拖放控件,修改属性

3.从本地上传文件

5.此时是图片正在上传图片,因为我们是在线编辑的,所以建议各位不要上传太大的图片

6.点击右上角的“Blocks Editor”按钮进入编辑界面,此时会显示正在下载,即从网站现在我们的程序,此时有可能会要求下载java6,点击下载即可,下载完成之后如果你的app inventor路径不是默认的,需要填写仿真器的路径

7.连接本人的android2.2手机效果如下

作者:神舟龙
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]ASP.NET MVC中页面中进行自定义文字截取

mikel阅读(913)

[转载]ASP.NET MVC中页面中进行自定义文字截取 – peter cheng – 博客园.

效果如下:

其实截取方法跟aspx页面中一样,不管是用C#还是js或者CSS都可以实现,

我在这里只是想记录在MVC中怎么用的两种方法:

方法一:

先新建一个类

然后编写以下方法:

public static class HtmlHelpers
{
public static string Truncate(this HtmlHelper helper, string input, int length)
{
if (input.Length &lt;= length)
{
return input;
}
else
{
return input.Substring(0, length) + "...";
}
}
}

在web.config中此处进行注册

添加以下信息:


最后就是在View页面中用了:

<%: Html.Truncate(item.Title, 25) %> (MVC2写法)

@Html.Truncate(item.Artist.Name, 25) (MVC3写法)

方法二(MVC3):

直接在View页面中:

@helper Truncate(string input, int length)
{
    if (input.Length <= length) {
        @input
    } else {
        @input.Substring(0, length)<text>...</text>
    }
}

然后调用这个方法:

@Truncate(item.Artist.Name, 25)