[转载]开发ERP软件应该遵守的22条规则 - James Li博客园

mikel阅读(979)

[转载]开发ERP软件应该遵守的22条规则 – James Li博客园.

总结一下做管理软件,有哪些项是经过检验的条款,必须遵守的。

界面篇

1  要保存用户的偏号(profile/favourite)。 ASP.NET 2.0引入此功能,当用户修改默认的控件的属性时,框架应当保存用户的修改。显而易懂的例子是grid控件中的列顺序。用户修改之后,关闭窗体时,要可以 保存起来,当用户再次打开时,应当加载用户上次的修改。

2  界面中的数据要有通一的导出方案。导出类型一般是Mircrosoft Excel, Microsoft Access, Text,CSV。Excel为第一需要考虑的导出格式。

3  半角全角输入转化。对于全角输入,应当转化为半角。全角半角长度不一样,但在界面中它的字符是一样的,应当默认的将用户全角的输入转化为半角。或是直接提示用户切换输入法,不允许全角输入。

4  使用标准的流行的报表开发技术和设计工具。自定义报表设计器很难控制稳定性,fyireport就是这样,即使有新功能也很难加入。再次,它的学习成本也比较高。推荐直接选择Crystal Report或是Reporting Services。

Crystal Report一直都没有大的改动,Reporting Services因为SQL Server昂贵的授权费用,因而产生了一些优秀的报表工具。但我仍然推荐用成熟的Crystal Report技术。

5  做好系统的三个门面窗体: Login, Splash,About。

登陆窗口的背景色要与主窗体一致,给用户的第一感觉的地方,要以稳重为主。我选择以微软网站的蓝色基调为主色。

Splash显示当前的程序的版本,授权用户信息。About对话框中显示license信息,以及客户服务联系方式。

6  界面中调用一项系统服务时,应该先检查它是否存在。比如即将显示报表界面,应当先检测报表服务是否存在,进行环境检测。运行事务时,应该先检测MSDTC 服务是否已经启动。以.NET 4.5 为Target生成程序版本时,需要先检测.NET 4.5是否已经安装。

7  保持与服务器的连接,断线后要禁用用户输入。这需要引入心跳机制。

 

 

数据库篇

1  给每个日记帐表添加额外的五个字段。用来保存这笔记录的创建人,创建时间,最后修改时间,修改日期:

Created_By, Created_Date, Revised_By, Revised_Date

另一个字段是为维护数据(data fix)时,方便使用:添加一个自增列,identity(1,1) ,设置为每一个表的第一列。名字可以是RECNUM(record number), LineNO(line number) 等等。

2  统一的数据位数方案。比如

行号类: LineNo/EntryNo/RecordNo/Recnum 不显示小数点,

数量类:Qty/Quantity 6位小数

金额类:Amt/Amount 4位小数

3  对于通用的数据表,比如物料主档,客户主档,供应商主档,应该保守的留20个备用字段,以供客户填写自定义的信息。再完善的数据库字段,都有可能考虑不周到的地方。20个备用字段,应该可以满足大部分需求。

4  修改数据库排序规则,一般默认为USA标准的SQL_LATING_CP1_CI_AS。这样可以减少存储过程出错的机率。

另外,在安装SQL Server时,也要选取这个排序规则。

 

加密保护篇

1 用户登陆表中的密码,不能以明文保存。可以用盐或是MD5加密,或是可逆的加密,或是字符串混淆(比如给每个字符都加一定规则的字符串,到检测密码时,再反过来还原密码)。

2  提供几种方式的用户验证机制。认证类型:PASSWORD, DOMAIN。

传统的密码验证需要输入密码,如果改成域DOMAIN验证,不用输密码,根据当前用户直接登陆系统。

3  完善的license许可授权机制。虚拟机检测,硬件检测,过期时间检测,功能限制检测,试用版过期检测。

4  混淆.NET程序集,增加反编译难度。

 

程序开发

1  提供通用的跟踪机制解决方案。比如应有Debug.WriteLine输出跟踪信息,再截获这个输出,显示到log viewer程序中。或是应用UDP端口发送跟踪信息,再捕获显示到界面中。下面的代码可做参考

Trace.Listeners.Add(new TextWriterTraceListener("TextWriterOutput.log", "myListener"));
Trace.TraceInformation("Test message.");
// You must close or flush the trace to empty the output buffer.
Trace.Flush();

2  提供通用的附件管理功能。附件可以上传到数据库中,也可以直接保存一个路径引用。同时,需要写一个附件浏览器,可查看所有的带附件的功能的内容。

3  界面中Tab键的顺序要合理,遵守从上到下,从左到右的顺序。还可以做到Enter转成Tab,回车间转成Tab键。

4   源代码中,数字类型的值的格式要统一。0x开头的16进制,默认的是10进制。这两个格式应当统一。可以使用Windows 7的计算器功能实现快速修改一个16时制数为10时制数。

5  提供标准的数据操作功能。

数据存档(Archieve):可以把数据导出为EXCEL或是其它的格式

数据清理(Cleanup):可以清除数据表

数据再开始(Restart):只清除日记帐数据,而保留系统设置和主档数据

数据导入(Load): 从备份文件中加载数据,相当于导入数据。

6 界面上长时间的操作,要转成后台线程。界面中可以有BackgroundWorker,代码逻辑中,应该调用System.Threading.Thread的后台线程来计算。与此同时,界面中的光标也需要改变

this.Cursor=Cursors.WaitCursor;
...... long operation
this.Cursor=Cursors.Default;

 

7  经常留意代码效率改善方法,并把它应用到系统中。举例如下

1) 不要用string ax=”” 判断ax是否为空, ax.Length==0的效率高于ax==string.Emptyu 一般用as.IsNullorEmpty
2) 对于不改变的变量,用常量代替
3)  用Linq代替大量的foreach查询
4)  MyType t=(MyType) t和t as MyType的效率比较
5)  List<T>代替Array,Haset<T> ,Dictionary<K,V> 代替Hashtable

 

[转载]MS SQL 日常维护管理常用脚本(一) - 潇湘隐者博客园

mikel阅读(1504)

[转载]MS SQL 日常维护管理常用脚本(一) – 潇湘隐者博客园.

SQL SERVER 数据库日常维护,管理,巡检过程中你可能经常需要用到一些SQL语句(亦或方法)来查看数据库服务器环境(操作系统版本, 磁盘空间,CPU,RAM信息),数据库信息(数据库版本,实例名称…),数据库对象等。

查看数据库信息

 

查看数据库服务器名称

  方法1:SQL脚本查询,可以通过下面脚本来查询。

  默认实例

默认实例查询
  1. SELECT @@SERVERNAME AS SERVERNAME;
  2. SELECT SERVERPROPERTY(‘servername’) AS ServerName;
  3. SELECT srvname AS ServerName FROM sys.sysservers;
  4. SELECT SERVERPROPERTY(‘MachineName’) AS ServerName

  命名实例

命名实例查询
  1. SELECT SUBSTRING(@@SERVERNAME, 0, CHARINDEX(‘\’, @@SERVERNAME))AS SERVERNAME;
  2. SELECT SUBSTRING(CONVERT(VARCHAR(100),SERVERPROPERTY(‘servername’)), 0, CHARINDEX(‘\’,CONVERT(VARCHAR(100),SERVERPROPERTY(‘servername’)))) AS ServerName;
  3. SELECT SUBSTRING(srvname, 0, CHARINDEX(‘\’, srvname)) AS ServerName FROM sys.sysservers;
  4. SELECT SERVERPROPERTY(‘MachineName’) AS ServerName

 

  方法2:在数据库实例单击右键,选择“属性”——》“常规”选项里面,你可以看到服务器名称(划红线部分)

clip_image002

 

  方法3:都不好意思说了,你懂的。

 

查看数据库实例名称

  方法1:去服务(services.msc)里面查找SQL Server(××××)这样的服务,有多少个就就有多少数据库实例,一般默认实例是SQL Server (MSSQLServer)

  方法2:去SQL配置管理器的SQL Server服务配置里面找上面描述的服务。

  方法3:脚本查询,只是截取数据库服务名称的实例名(其实这个还真没必要,通过上面的脚本就可查看实例,注意默认实例)

Code Snippet
  1. SELECT @@SERVICENAME AS InstantName;
  2. SELECT ISNULL(SERVERPROPERTY(‘InstanceName’),‘MSSQLServer) AS InstanceName;

   下面脚本仅对命名实例有效,默认实例查询处理的是计算机名称

Code Snippet
  1. SELECT SUBSTRING(@@SERVERNAME,CHARINDEX(‘\’, @@SERVERNAME)+1,100) AS InstantName;
  2. SELECT SUBSTRING(srvname, CHARINDEX(‘\’, srvname) +1, 100) AS InstantName FROM sys.sysservers;

 

查看数据库版本号

方法1:SQL 1:

Code Snippet
  1. SELECT    SERVERPROPERTY(‘productversion’) AS ProductVersion ,
  2.           SERVERPROPERTY(‘productlevel’) AS ProductLevel ,
  3.           SERVERPROPERTY(‘edition’) AS Edition

方法2:SQL 2: 看起来比较麻烦

Code Snippet
  1. SELECT @@VERSION AS PRODUCT_VERSION;

Microsoft SQL Server 2005 – 9.00.4035.00 (Intel X86)   Nov 24 2008 13:01:59

  Copyright (c) 1988-2005 Microsoft Corporation         

Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)

如何从 9.00.4035.00 (Intel X86) 读取一些信息呢?

  第一个数字:8 代表2000版本, 9 代表2005版本, 10 代表2008 版本

  第二个数字:50 代表R2版本, 00 代表非R2版本

  第三个数字:如上所示4035中第一个数字4代表打了补丁SP3,其规则如下所示

      1: 代表没有打补丁

      2: 代表打了SP1补丁

      3: 代表打了SP2补丁,以此类推

      035 : 代表相关的hotfix版本,我们在进行升级,备份,恢复一定要看清楚这个版本,只有在同一版本下的相关数据才能进行恢复(同版本备份文件),这点要注意。

方法3:在数据库实例上单击右键,选择“属性”——》“常规”选项里面,你可以看到服务器名称,平台,操作系统,数据库版本信息。

 

查看数据库已经打的补丁

方法1:如上所示,可以通过9.00.4035.00 (Intel X86) 来确定已经数据库已经打的最新补丁SP3。

方法2:在数据库实例上单击右键,选择“属性”——》“常规”选项里面,可以通过看到的版本信息查看补丁

方法3:在SQL配置管理器,找到相应的实例的SQL Server服务,单击右键属性.

clip_image004

 

查看实例数据库的相关信息

查看实例有哪些数据库,创建时间、排序规则…….

  方法1:SQL 查询. 其实在视图sys.databases里面你可以查看很多关于数据库的信息,例如,数据库用户访问设置,数据库的状态……

  SELECT * FROM sys.databases

  方法2: 在Mircrosoft SQL Server Management Studio管理器里面查看。

查看排序规则信息

1:查看实例排序规则

    SELECT SERVERPROPERTY(N’Collation’)

2:查看数据库排序规则

    SELECT name, collation_name FROM sys.databases

查询当前数据库的磁盘使用情况

如需要查询其他数据库,则需在前面指定数据库名称

    EXEC sp_spaceused;

查看数据库启动相关参数

    EXEC sp_configure;

查看数据库启动时间

方法1:

Code Snippet
  1. SELECT CONVERT(VARCHAR(30), LOGIN_TIME,120) AS StartDateTime
  2. FROM master..sysprocesses WHERE spid=1

查看所有数据库名称及大小

方法1:

  EXEC sp_helpdb;

方法2:

Code Snippet
  1.   SELECTdatabase_id AS DataBaseId ,
  2.         DB_NAME(database_id) AS DataBaseName ,
  3.         CAST(SUM(SIZE) * 8.0 / 1024 AS DECIMAL(8, 4)) AS [Size(MB)]
  4.     FROMsys.master_files
  5. GROUP BY database_id

 

查看所有数据库用户登录信息

  EXEC sp_helplogins;

查看所有数据库用户所属的角色信息

  EXEC sp_helpsrvrolemember

更改某个数据对象的用户属主

  sp_changeobjectowner [@objectname =] ‘object’, [@newowner =] ‘owner’

注意:更改对象名的任一部分都可能破坏脚本和存储过程。把一台服务器上的数据库用户登录信息备份出来可以用add_login_to_aserver脚本。查看某数据库下,对象级用户权限sp_helprotect

查看链接服务器

  EXEC sp_helplinkedsrvlogin

查看远端数据库用户登录信息

  sp_helpremotelogin

查看数据库下某个数据对象的大小

  sp_spaceused @objname

查看某数据库下某个数据对象的索引信息

  sp_helpindex @objname

查看某数据库下某个数据对象的的约束信息

  sp_helpconstraint @objname

查看表的相关信息

方法1:

    sp_help ‘TABLE_NAME’

方法2:

    sp_desc  参考我的博客MS SQL 模仿ORACLE的DESC

修复迁移服务器时孤立用户时

方法1:

  USE {目标数据库}

  EXEC sp_change_users_login ‘Update_One’, ‘{目标数据库已存在的用户名}’, ‘{创建的登录用户名}’

方法2

Code Snippet
  1. declare @cmd nvarchar(4000)
  2. set @cmd = N’exec [?].sys.sp_change_users_login @Action = ”Auto_Fix”
  3. , @UserNamePattern = ”qa”
  4. , @LoginName = null
  5. , @Password = ”abc” ‘
  6. exec sp_msforeachdb@cmd

查看数据库数据文件情况

查看数据库实例各个数据库的数据文件信息

方法1: 选择某个数据库,然后单击右键属性…(后面我就不说了,不知道的自己百度)

方法2:SQL

Code Snippet
  1. SELECT database_id                    AS DataBaseId   ,
  2.       DB_NAME(database_id)           AS DataBaseName ,
  3.       Name                           AS LogicalName  ,
  4.       type_desc                      AS FileTypeDesc ,
  5.       Physical_Name                  AS PhysicalName ,
  6.       State_Desc                     AS StateDesc ,
  7.       CASE WHEN max_size = 0  THEN N’不允许增长
  8.            WHEN max_size = 1 THEN N’自动增长
  9.            ELSE LTRIM(STR(max_size * 8.0 / 1024 / 1024, 14, 2)) + ‘G’
  10.       END                            AS MaxSize ,
  11.       CASE WHEN is_percent_growth = 1
  12.            THEN RTRIM(CAST(Growth AS CHAR(10))) + ‘%’
  13.            ELSE RTRIM(CAST(Growth AS CHAR(10))) + ‘M’
  14.       END                            AS Growth ,
  15.       Is_Read_Only                   AS IsReadOnly ,
  16.       Is_Percent_Growth              AS IsPercentGrowth ,
  17.       CAST(size * 8.0 / 1024 / 1024 AS DECIMAL(8, 4)) AS [Size(GB)]
  18.   FROM sys.master_files

查看单个数据库的数据文件信息:

      SQL 1:上面SQL加上查询条件

      SQL 2:

Code Snippet
  1. SELECT    Name                                     AS DataBaseName ,
  2.           Physical_Name                            AS PhysicalName ,
  3.           type_desc                                AS FileTypeDesc ,
  4.           State_Desc                               AS StateDesc    ,
  5.           (( size * 8.0 ) / 1024 / 1024 )          AS [Size(GB)]   ,
  6.           CASE WHEN max_size = 0  THEN N’不允许增长
  7.                WHEN max_size = 1 THEN N’自动增长
  8.                ELSE LTRIM(STR(max_size * 8.0 / 1024 / 1024, 14, 2)) + ‘G’
  9.           END AS MaxSize ,
  10.           CASE WHEN is_percent_growth = 1
  11.                THEN RTRIM(CAST(Growth AS CHAR(10))) + ‘%’
  12.                ELSE RTRIM(CAST(Growth AS CHAR(10))) + ‘M’
  13.           END AS Growth ,
  14.           Is_Read_Only                             AS IsReadOnly      ,
  15.           Is_Percent_Growth                        AS IsPercentGrowth ,
  16.           CAST(size * 8.0 / 1024 / 1024 AS DECIMAL(8, 4)) AS [Size(GB)]
  17. FROM      sys.database_files ;

      SQL 3:

Code Snippet
  1. SELECT fileid        AS FileId       ,
  2.         groupid      AS GroupId      ,
  3.         size         AS DataBaseSize ,
  4.         growth       AS Growth       
  5.         perf         AS Perf         ,
  6.         name         AS NAME         ,
  7.         filename     AS FILENAME
  8. FROM   MESDB.dbo.sysfiles ;

查看数据库服务器各数据库日志文件的大小及利用率/状态

    DBCC SQLPERF(LOGSPACE)

查看当前数据库的文件状态

    EXEC (‘DBCC showfilestats’) 

查看数据库存储过程

查看有哪些存储过程

方法1:

  EXEC sp_stored_procedures;

方法2:

  SELECT * FROM sys.procedures;

方法3:

  SELECT * FROM sys.sysobjects WHERE xtype=’P’;

查看存储过程基本信息

EXEC sp_help ‘dbo.sp_who_lock’

查看存储过程源代码

方法1:

    EXEC sp_helptext ‘procedureName’

方法2:

    SELECT  *

    FROM    SYS.SQL_MODULES

    WHERE   object_id = OBJECT_ID(N’procedureName‘)

方法3:

Code Snippet
  1.  SELECT s.text                     AS ProcedureText ,
  2.         s.encrypted                AS Encrypted ,
  3.         s.number                   AS number ,
  4.         CONVERT(NCHAR(2), o.xtype) AS xtype ,
  5.         DATALENGTH(s.text)         AS ProcedureLen
  6.     FROMdbo.syscomments s ,
  7.         dbo.sysobjects o
  8.    WHEREo.id = s.id
  9.         AND s.id = OBJECT_ID(N’procedureName)
  10. ORDER BY s.number ,
  11.         s.colid
  12. OPTION  ( ROBUST PLAN )

服务器环境信息

查看数据库所在机器操作系统参数

  方法1:

    EXEC master..xp_msver

详解:xp_msver返回有关 Microsoft SQL Server 的版本信息。xp_msver 还返回有关服务器的实际内部版本号的信息以及服务器环境的有关信息,例如处理器类型(不能获取具体型号), RAM 的容量等等。用脚本基本上很难获取详细的硬件信息。

  方法2:登录服务器,直接查看服务器信息。

查看数据库服务器磁盘分区剩余空间。

  方法1:

    EXEC master.dbo.xp_fixeddrives;

  方法2:登录服务器直接查看或用工具

  方法3:请看MS SQL 监控磁盘空间告警里面介绍的方法

查看数据库服务器磁盘容量信息

  方法1:请看MS SQL 监控磁盘空间告警里面介绍的方法

  方法2:登录服务器直接查看或用工具

查看数据库服务器CPU/内存的大概信息

 

Code Snippet
  1.  SELECT cpu_count                          AS [Logical CPU Count] ,
  2.         hyperthread_ratio                  AS [Hyperthread Ratio] ,
  3.         cpu_count / hyperthread_ratio      AS [Physical CPU Count],
  4.         physical_memory_in_bytes / 1048576 AS [Physical Memory (MB)] ,
  5.         SQLServer_start_time
  6.     FROMsys.dm_os_sys_info
  7. OPTION  ( RECOMPILE ) ;

 

小结:用SQL查看服务器硬件信息,似乎不是个好主意,很难得到精确地信息,例如CPU型号、内存条的频率

[转载]游戏辅助编程之神器CE的使用 - egojit博客园

mikel阅读(1175)

[转载]游戏外挂编程之神器CE的使用 – egojit博客园.

游戏外挂用过,但是有没多少人知道怎么去实现,既然我们都是草根程序员,没那钱去买那就自己去写。外挂据我的了解最简单的一种就是模拟鼠标点击。比 如MM们都喜欢玩的那种连连看游戏只要鼠标点击就OK了,所以只需要正确的模拟鼠标点击就行了。分析出游戏内存然后再正确的位置去模拟鼠标点击就能在一秒 以内完成连连看。还有比较高级点的就是分析出游戏相关的Call然后去将远程代码注入到游戏进程空间中,将远程代码做为游戏进程代码的一部分去执行就行 了。当然是执行分析的Call。后一种比较麻烦,功能也比较强大,不仅要了解windows API,还得了解基本的操作系统原理和分页机制,然后还要会汇编编程,当然能实现高级的外挂。第一种只要了解Windows API就行了。

但是不管哪种方式我们都需要分析进程内存,我们写外挂是不知道程序的源代码,能了解到的就是汇编编译器放汇编出的汇编代码(以后再说)。最主要我们 需要知道我们感兴趣的东西放在进程空间的哪个内存地址中(这里的内存肯定不是物理内存,现在的一般32位系统都是在保护模式下的而非实模式)。为了查找内 容放在的内存地址,我要介绍一款内存神器,这是外挂界的法宝之一,CE。CE不仅能查看内存还能修改内存内容。我们现在就开始学习CE的使用技巧。这里就 以QQ为例吧。查出QQ消息发送之前的信息放在内存的什么地方。查到了我们就可以动态修改发送之前的消息,这样我们就可以发送我们想要的任何信息而不需要 通过qq.首先打开QQ发送窗口写一些信息,就写“Egojit”,打开神器CE。界面如下:

第一步:点击小电脑打开进程列表附件我们的QQ进程。

第二步:点击打开我们附加的QQ进程

第三步:(如下图)在数值中输入我们在QQ消息发送框中输入的”Egojit”

第四步:点击CE中的首次扫描(一定要选字符串)

首次扫描后悔出现很多内存中都有“Egojit”这时候我们需要做的就是第五步

第五步:改变QQ发送框中的信息为”Egojit1″,并且修改文本为“Egojit1”,别点击“新的扫描”,而是点击“再次扫描”。再次扫描就是在第一次的结果上再筛选

这次得到一个地址

为了确保准确信,可以多改变几次QQ发送框中的信息,然后再次搜索。我们双击结果。将它放到下面的CE下面的空白备选区中(我这么叫的)。

当查询结果通过双击加到备选区后。我们修改备选区的值。双击备选区的值字段弹出修改内存窗体。记住必须双击值部分,双击其它部分例如类型或者地址字 段都是不行的。将“Egojit1”修改成“gaolu123”,你成功的将内存中的改变了,并且消息框中的值也被改变了。可以以外的发现少了一个3

这是为什么呢??其实很容易理解。因为请对比修改前的类型和修改后的类型一个是String[7],修改后的是String[8]。这个时候你会知 道QQ消息显示框有一种限制,就是防止这种修改发送。(个人猜测)。不用怕。既然有限制,那么QQ内存中肯定有相关的存储计数器。再通过CE去查询,这个 时候不是查询字符串了,这个时候查询int.而且查询是一次新的查询。以上种种都是猜测。这个时候用CE去验证猜测。QQ发送框中现在有 “gaolu12”,数一下是7个字符。第一次查询:

大家看到查询结果中查询后有其它值,其实不是,在我第一次查询的时候只有7,只不过在我截图的这段时间中有些内存的值改变了所以就有0了。这个不用管。我们再次筛选删除掉QQ发送框中的一个字符这个时候计数器应该是6我们再次筛选6

大家很容易看到第二次筛选后还有14条结果。我们继续这样筛选。最后我们不管怎么筛选都还有11个结果。

而且我们动态去修改QQ发送框的字符个数,你发现这写内存都跟着变化。那我们就大胆猜测这些内存就是计数器,只不过不同的内存做着不同的约束。我们 不用管,我们绕过这些约束,修改所有的技术器。这样我们将这些技术器的数据修改成和上面修改的消息类型例如类型String[8]那么就将计数器中的2全 部改成8这样,QQ发送框中你不需要做任何修改。但是QQ发送框中的消息被修改了。这样我们就很容易去利用进程注入去修改这些内存,然后……。当然是你想 干什么就干什么,任你摆布了。

我们找出是什么访问消息内存。当然我们在qq消息框中击发送时肯定访问了这块内存,因为发送消息出去,发送的就是我们查询出的消息内存中的内容。我 们找到这段汇编代码。找到它的CALL我们就可以实现一个自动发送QQ信息的外挂了。这个后面在继续……。如果认真看了并且实验了,我相信你已经基本上会 使用CE了。恭喜你,开始了解一点本质了。

[转载]ASP.NET MVC中SWFUpload大文件上传 - 无聊的过客博客园

mikel阅读(1093)

[转载].net mvc 大文件上传 – 无聊的过客博客园.

因客户需要上传大文件,传统的mvc文件上传方法无法满足当前需求。所以哥就只能在网搜现成的列子,找了半天也没有找到好的demo.

大部分多是.net webFrom的例子。都是封装好的控件,顿时郁闷。

在网上虽然没有找到好的例子,但也在找到很好的解决方法那就是使用 swfUpload控件。swfUpload是JavaScript和falsh的合体。网上推荐指数比较高,资料也比较多。

中文api网址:http://leeon.me/upload/other/swfupload.html

1.想看截图。

image

2.swfUpload比较灵活,可自由扩展,就是需要花时间熟悉。本人花了半天时间才理清楚。官网上也有。net例子也是webFrom的

3.废话少说直接看代码:控制层:
public Guid Swfupload()
{
string uploadsFolder = HttpContext.Server.MapPath(“~/Upload”);
Guid identifier = Guid.NewGuid();
var uploadsPath = Path.Combine(uploadsFolder, identifier.ToString());
var httpfile = Request.Files[“Filedata”];
if (httpfile != null)
{
httpfile.SaveAs(uploadsPath);
}
return identifier;
}

4.显示层可以看官网的例子。还是挺好的。本人结合JQueryUI控件又写了一个例子。主要是替换官网的进度条。

是点击上传时,在对话框里显示上传进度。

@{
    ViewBag.Title = "文件上传Demo";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@section Head{
    <script src="/Scripts/SwfUpload/swfupload.js" type="text/javascript"></script>
    <script src="/Scripts/SwfUpload/plugins/swfupload.queue.js" type="text/javascript"></script>
    <script src="/Scripts/SwfUpload/plugins/swfupload.speed.js" type="text/javascript"></script>
    <script type="text/javascript">
        var swfu;
        window.onload = function () {
            swfu = new SWFUpload({
                upload_url: "/Home/Swfupload",
                flash_url: "/Scripts/SwfUpload/Flash/swfupload.swf",
                file_size_limit: "1500 MB",
                file_types: "*.*",
                file_types_description: "All Files",
                file_upload_limit: 0,
                file_queue_limit: 1,
                debug: false,

                // Button settings
                button_image_url: "/Scripts/SwfUpload/Images/TestImageNoText_65x29.png",
                button_width: "65",
                button_height: "29",
                button_placeholder_id: "spanButtonPlaceHolder",
                button_text: '<span class="theFont">浏览</span>',
                button_text_style: ".theFont { font-size: 16; }",
                button_text_left_padding: 12,
                button_text_top_padding: 3,


                file_dialog_complete_handler: function (numFilesSelected, numFilesQueued) {
                },
                file_queued_handler: function (file) {
                    $("#fileName").val(file.name);
                },
                upload_progress_handler: function (file, complete, total) {
                    var value = complete / total * 100;
                    $("#progressbar").progressbar("value", value);
                    $("#CurrentSpeed").html(SWFUpload.speed.formatBPS(file.currentSpeed));
                },
                upload_success_handler: function (file, data) {
                    alert("文件上传成功");
                    $("#fileProgress").dialog("close");
                }

            });
            $("#btnSubmit").click(function () {
                if (swfu.getStats().files_queued > 0) {
                    $("#fileProgress").dialog({
                        modal: true,
                        width: 400,
                        open: function () {
                            $("#progressbar").progressbar({
                                value: 0
                            });

                            swfu.startUpload();

                        }
                    });
                }

            });

            $("#cancelButton").click(function () {
                swfu.stopUpload();
            });
        };
    </script>
}
<fieldset>
    <legend>文件上传Demo</legend>
    <div>
        <input type="text" id="fileName" />
        <span id="spanButtonPlaceHolder">浏览</span>
        <button id="btnSubmit">
            上传</button>
    </div>
</fieldset>
<div title="文件上传进度" id="fileProgress" style="display: none">
    <div id="progressbar">
    </div>
    <button id="cancelButton">
        取消上传</button>
    当前上传速度: <span id="CurrentSpeed"></span>
</div>

[转载]Web前端,高性能优化 - 庄丶大虾博客园

mikel阅读(870)

转载Web前端,高性能优化 – 庄丶大虾博客园.

一、避免使用iframe
iframe也叫内联frame,可将一个HTML文档嵌入另一个HTML文档中。
iframe的好处是,嵌入的文档独立于父文档,通常也借此使浏览器模拟多线程。缺点是:

①虽然iframe能模拟多线程,但主流浏览器的同域名并行下载数是不变的,浏览器对同域名的链接总是共享浏览器级别的连接池,
即使是不同窗口或标签页的同域名网页。
②在页面加载时,iframe会阻塞父文档onload事件的触发。并且有些浏览器需在触发onload事件后才能被触发onunload事件。
故用户用onload事件长久未触发而离开页面时,不会触发onunload事件。
※不兼容IE6~8的解决方案:使用JavaScript动态加载iframe元素或动态设置其src属性。

<iframe id=ifr ></iframe>
document.getElementById( ‘ifr’ ).setAttribute( ‘src’ , ‘url ’ );

③iframe是文档内最消耗支援的元素之一,即使是空iframe的开销也是昂贵的。【通过Steve Souders测试】

二、避免空连接属性
空连接指:img、link、script 和 iframe元素的src或href属性的值为空。(如src = ””)
设置了空连接后浏览器依然会以默认规则发送请求:
①IE6~8中只有img元素会出问题:IE会将img的空地址解析为当前页面地址的目录地址并请求。
如当前网页地址为http://aaa.com/bb/c.html,img的地址会被解析为http://aaa.com/bb
②早些版本的Webkit和Firefox会将空连接解析为当前页面的地址。在ios与Android中此问题较严重。
如果页面有多个空连接属性元素,会增加服务器的请求次数。
③幸运的是,主流浏览器对iframe的src属性值为空时,会解析为about:blank地址,而不发送额外请求。

三、避免节点深层级嵌套
层级越深的节点在初始化构建时,所占内存越多。
通过浏览器HTML解析器会将整个HTML文档的结构存储为DOM树结构。当节点嵌套层次越深,构建的DOM书层次也越深。

四、缩减HTML文档大小
①删除对执行结果无影响的空格空行和注释;
②避免table布局;
③使用HTML5;

五。显式指定文档字符集
在HTML页面开时指定字符集有助于浏览器立即开始解析HTML代码。
HTML文档通常被解析为一序列的带字符集编码信息的字符串,通过Internet传送。
字符集编码在HTTP响应头中,或HTML标记中指定。浏览器通过指定的字符集,吧编码解析为可现实在屏幕上的字符。
若浏览器无法获知页面的编码字符集,一般会在执行脚本和渲染页面之前,先将字节流缓存,再搜索可进行解析的字符集 或 以默认字符集来解析。

六、显示设置图片的宽高
有时需要在页面加载完之前,就对页面布局进行定位。
若页面中的图片没指定尺寸,或尺寸与实际图片大小不符,浏览器会在图片下载完成后再”回溯”该图片并重新显示,从而浪费时间。
故最好为页面的图片设置指定尺寸(行内样式或CSS样式)。

<img src="hello.png" width="400" height="300">

七、避免 脚本阻塞加载
浏览器在解析常规script标签时,会等待script下载完毕后,才解析执行,之后的HTML代码就只能等待。
故应该将脚本放在文档的末尾:

 <script src="example.js" ></script>
 </body>

 

 

高性能CSS

一、避免使用@import
CSS2.1加入的@import,会使页面在加载时添加额外延迟。
由于浏览器不能并行下载样式,会导致页面增添额外的往返耗时。而使用<link>能并行下载样式,但任然是多次请求。

二、避免AlphaImageLoader滤镜
此滤镜能解决IE6即一下版本显示PNG图片的半透明效果,但会在加载图片时终止内容的呈现,并冻结浏览器。
在每个元素(不仅仅是图片)都会运算一次,添加内存开支。
应使用PNG8格式来代替,或用下划线(_filter)只针对IE6。

三、避免CSS表达式
CSS表达式是设置动态CSS属性的即强调又危险的方法。IE5开始支持,IE独有。

//实现每隔一小时切换一次背景颜色
background-color: expression((new Date()).getHours()%2?"#FFFFFF": "#000000" );

CSS表达式的缺点是技术频率极大,在页面显示、缩放、滚动 或 移动鼠标,都会重新计算一次。移动随便会达到1w次以上的计算量。
①使用一次性的表达式能减少计算次数,在第一次运行时将结果赋给指定样式属性,并用该属性代替CSS表达式。
②如果样式属性必须在页面周期内动态地改变,使用时间句柄代替CSS表达式是一个可行的办法。

四、避免通配选择器
优化选择器的原则是减少匹配时间。CSS选择器的匹配机制是:从右向左进行规制匹配的!
#header > a { font-weight:blod; }
上面这条规制实际是浏览器遍历页面所有a元素,并确定其父元素的id是否为header。
#header  a {…}
后代选择器开销更大,在遍历页面的所有a元素后,会需向上遍历直到根节点。
由此可知,选择器最右边的规制 往往决定了向左移匹配的工作量。故最右边的选择规则 称之为关键选择器。
.selected * {…}
在匹配所有元素后,再分别向上匹配直至根节点。通常比开销最小的ID选择器高出·~3个数量级。

五、避免单规则的属性选择器
.selected [href=’#index’] {…}
浏览器先匹配所有的元素,检查其是否有href属性并且值为“#index”,再分别向上匹配class为selected的元素。
故应该避免关键选择器 使用单规则属性选择器。

六、避免正则的属性选择器
CSS3添加了复杂的属性选择器,通过类正则表达式进行匹配。但这些类型的选择器会比基于类别的匹配慢很多。

七、移除无匹配的样式
①删除无用的样式,可缩减样式文件大小,加快加载速度。
②对于浏览器,所有样式规则都会被解析后索引起来,即使是当前页面无匹配的规则!故移除无匹配的规则,减少索引项,加快浏览器查找速度。

 

高性能JavaScript

一、使用事件代理
当过多的时间句柄被频繁触发时,页面反应会迟钝。
如一个div有10个按钮,只需给div附加一次事件句柄,而不必给每个按钮添加一个句柄。
事件冒泡时刻捕捉到事件 并判断时那个事件发出的。【触发事件的元素 = ev.srcElement ? ev.srcElement : ev.target;】

二、缓存选择器查询结果
减少选择器查询的次数,并尽可能缓存选中的结果,便于以后的重用。

复制代码
jQuery('#top').find('p.classA'); ... jQuery('#top').find('p.classB'); //使用下面的方法 减少开销
var cached = jQuery('#top'); cached.find('p.classA'); ... cached.find('p.classB');
复制代码

三、避免频繁的IO操作
应减少对cookie或localstorage的操作,因为对它们进行操作的API是同步的,而它们是多个tab页面间共享的。
多页面同时操作cookie和localstorage时,会存在同步加锁机制。

四、避免频繁的DOM操作
JavaScript访问DOM元素缓慢,应做到:
①缓存已经查询过的元素;
②线下更新完节点之后,在将它们添加到文档树中;
③避免使用JavaScript来修改页面布局。

五、使用微类库
尽量避免使用大而全的类库,而是按需使用微类库来辅助开发。

 

本文整理自:http://madscript.com/performance/high-performance-frontend/

[转载]浏览器的渲染原理简介 | 酷壳 - CoolShell.cn

mikel阅读(942)

[转载]浏览器的渲染原理简介 | 酷壳 – CoolShell.cn.

看到这个标题大家一定会想到这篇神文《How Browsers Work》,这篇文章把浏览器的很多细节讲得很细,而且也被翻译成了中文。为什么我还想写一篇呢?因为两个原因,

1)这篇文章太长了,阅读成本太大,不能一口气读完。

2)花了大力气读了这篇文章后可以了解很多,但似乎对工作没什么帮助。

所以,我准备写下这篇文章来解决上述两个问题。希望你能在上班途中,或是坐马桶时就能读完,并能从中学会一些能用在工作上的东西。

浏览器工作大流程

废话少说,先来看个图:

从上面这个图中,我们可以看到那么几个事:

 

1)浏览器会解析三个东西:

  • 一个是HTML/SVG/XHTML,事实上,Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。
  • CSS,解析CSS会产生CSS规则树。
  • JavaScript,脚本,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.

2)解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意:

  • Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。
  • CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。
  • 然后,计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程。

3)最后通过调用操作系统Native GUI的API绘制。

DOM解析

HTML的DOM Tree解析如下:

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<html>
<head>
    <title>Web page parsing</title>
</head>
<body>
    <div>
        <h1>Web page parsing</h1>
        <p>This is an example Web page.</p>
    </div>
</body>
</html>

上面这段HTML会解析成这样:

下面是另一个有SVG标签的情况。

CSS解析

CSS的解析大概是下面这个样子(下面主要说的是Gecko也就是Firefox的玩法),假设我们有下面的HTML文档:

1
2
3
4
5
6
7
8
9
<doc>
<title>A few quotes</title>
<para>
  Franklin said that <quote>"A penny saved is a penny earned."</quote>
</para>
<para>
  FDR said <quote>"We have nothing to fear but <span>fear itself.</span>"</quote>
</para>
</doc>

于是DOM Tree是这个样子:

然后我们的CSS文档是这样的:

1
2
3
4
 /* rule 1 */ doc { display: block; text-indent: 1em; }
/* rule 2 */ title { display: block; font-size: 3em; }
/* rule 3 */ para { display: block; }
/* rule 4 */ [class="emph"] { font-style: italic; }

于是我们的CSS Rule Tree会是这个样子:

注意,图中的第4条规则出现了两次,一次是独立的,一次是在规则3的子结点。所以,我们可以知道,建立CSS Rule Tree是需要比照着DOM Tree来的。CSS匹配DOM Tree主要是从右到左解析CSS的Selector,好多人以为这个事会比较快,其实并不一定。关键还看我们的CSS的Selector怎么写了。

注意:CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以,你就会在N多地方看到很多人都告诉你,DOM树要小,CSS尽量用id和class,千万不要过渡层叠下去,……

通过这两个树,我们可以得到一个叫Style Context Tree,也就是下面这样(把CSS Rule结点Attach到DOM Tree上):

所以,Firefox基本上来说是通过CSS 解析 生成 CSS Rule Tree,然后,通过比对DOM生成Style Context Tree,然后Firefox通过把Style Context Tree和其Render Tree(Frame Tree)关联上,就完成了。注意:Render Tree会把一些不可见的结点去除掉。而Firefox中所谓的Frame就是一个DOM结点,不要被其名字所迷惑了

注:Webkit不像Firefox要用两个树来干这个,Webkit也有Style对象,它直接把这个Style对象存在了相应的DOM结点上了。

渲染

渲染的流程基本上如下(黄色的四个步骤):

  1. 计算CSS样式
  2. 构建Render Tree
  3. Layout – 定位坐标和大小,是否换行,各种position, overflow, z-index属性 ……
  4. 正式开画

注意:上图流程中有很多连接线,这表示了JavaScript动态修改了DOM属性或是CSS属会导致重新Layout,有些改变不会,就是那些指到天上的箭头,比如,修改后的CSS rule没有被匹配到,等。

这里重要要说两个概念,一个是Reflow,另一个是Repaint。这两个不是一回事。

  • Repaint——屏幕的一部分要重画,比如某个CSS的背景色变了。但是元素的几何尺寸没有变。
  • Reflow——意味着元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫reflow)reflow 会从<html>这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,在reflow过程中,可能会增加一些frame,比如一个文本字符串必需被包装起来。

下面是一个打开Wikipedia时的Layout/reflow的视频(注:HTML在初始化的时候也会做一次reflow,叫 intial reflow),你可以感受一下:

Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。在一些高性能的电脑上也许还没什么,但是如果reflow发生在手机上,那么这个过程是非常痛苦和耗电的

所以,下面这些动作有很大可能会是成本比较高的。

  • 当你增加、删除、修改DOM结点时,会导致Reflow或Repaint
  • 当你移动DOM的位置,或是搞个动画的时候。
  • 当你修改CSS样式的时候。
  • 当你Resize窗口的时候(移动端没有这个问题),或是滚动的时候。
  • 当你修改网页的默认字体时。

注:display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。

多说两句关于滚屏的事,通常来说,如果在滚屏的时候,我们的页面上的所有的像素都会跟着滚 动,那么性能上没什么问题,因为我们的显卡对于这种把全屏像素往上往下移的算法是很快。但是如果你有一个fixed的背景图,或是有些Element不跟 着滚动,有些Elment是动画,那么这个滚动的动作对于浏览器来说会是相当相当痛苦的一个过程。你可以看到很多这样的网页在滚动的时候性能有多差。因为 滚屏也有可能会造成reflow。

基本上来说,reflow有如下的几个原因:

  • Initial。网页初始化的时候。
  • Incremental。一些JavaScript在操作DOM Tree时。
  • Resize。其些元件的尺寸变了。
  • StyleChange。如果CSS的属性发生变化了。
  • Dirty。几个Incremental的reflow发生在同一个frame的子树上。

好了,我们来看一个示例吧:

1
2
3
4
5
6
7
8
9
10
11
12
var bstyle = document.body.style; // cache
bstyle.padding = "20px"; // reflow, repaint
bstyle.border = "10px solid red"; //  再一次的 reflow 和 repaint
bstyle.color = "blue"; // repaint
bstyle.backgroundColor = "#fad"; // repaint
bstyle.fontSize = "2em"; // reflow, repaint
// new DOM element - reflow, repaint
document.body.appendChild(document.createTextNode('dude!'));

当然,我们的浏览器是聪明的,它不会像上面那样,你每改一次样式,它就reflow或repaint一次。一般来说,浏览器会把这样的操作积攒一批,然后做一次reflow,这又叫异步reflow或增量异步reflow。但是有些情况浏览器是不会这么做的,比如:resize窗口,改变了页面默认的字体,等。对于这些操作,浏览器会马上进行reflow。

但是有些时候,我们的脚本会阻止浏览器这么干,比如:如果我们请求下面的一些DOM值:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop/Left/Width/Height
  3. clientTop/Left/Width/Height
  4. IE中的 getComputedStyle(), 或 currentStyle

因为,如果我们的程序需要这些值,那么浏览器需要返回最新的值,而这样一样会flush出去一些样式的改变,从而造成频繁的reflow/repaint。

减少reflow/repaint

下面是一些Best Practices:

1)不要一条一条地修改DOM的样式。与其这样,还不如预先定义好css的class,然后修改DOM的className。

1
2
3
4
5
6
7
8
9
10
11
// bad
var left = 10,
top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";
// Good
el.className += " theclassname";
// Good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

2)把DOM离线后修改。如:

  • 使用documentFragment 对象在内存里操作DOM
  • 先把DOM给display:none(有一次reflow),然后你想怎么改就怎么改。比如修改100次,然后再把他显示出来。
  • clone一个DOM结点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下。

3)不要把DOM结点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。

4)尽可能的修改层级比较低的DOM。当然,改变层级比较底的DOM有可能会造成大面积的reflow,但是也可能影响范围很小。

5)为动画的HTML元件使用fixed或absoult的position,那么修改他们的CSS是不会reflow的。

6)千万不要使用table布局。因为可能很小的一个小改动会造成整个table的重新布局。

In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.

Fixed layout, CSS 2.1 Specification

This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.

Automatic layout, CSS 2.1 Specification

几个工具和几篇文章

有时候,你会也许会发现在IE下,你不知道你修改了什么东西,结果CPU一下子就上去了到100%,然后过了好几秒钟repaint/reflow才完成,这种事情以IE的年代时经常发生。所以,我们需要一些工具帮我们看看我们的代码里有没有什么不合适的东西。

  • Chrome下,Google的SpeedTracer是个非常强悍的工作让你看看你的浏览渲染的成本有多大。其实Safari和Chrome都可以使用开发者工具里的一个Timeline的东东。
  • IE下你可以用一个叫dynaTrace的IE扩展。

最后,别忘了下面这几篇提高浏览器性能的文章:

参考

[转载]Javascript 装载和执行 | 酷壳 - CoolShell.cn

mikel阅读(794)

[转载]Javascript 装载和执行 | 酷壳 – CoolShell.cn.

一两个月前在淘宝内网里看到一个优化JavaScript代码的竞赛,发现有不少的人对JavaScript的执行和装载的基础并不懂,所以,从那天起我就想写一篇文章,但一直耽搁了。自上篇《浏览器渲染原理简介》,正好也可以承前启后。

首先,我想说一下JavaScript的装载和执行。通常来说,浏览器对于Javascript的运行有两大特性:1)载入后马上执行,2)执行时会阻塞页面后续的内容(包括页面的渲染、其它资源的下载)。于是,如果有多个js文件被引入,那么对于浏览器来说,这些js文件被被串行地载入,并依次执行。

因为javascript可能会来操作HTML文档的DOM树,所以,浏览器一般都不会像并行下载css文件并行下载js文件,因为这是js文件的 特殊性造成的。所以,如果你的javascript想操作后面的DOM元素,基本上来说,浏览器都会报错说对象找不到。因为Javascript执行时, 后面的HTML被阻塞住了,DOM树时还没有后面的DOM结点。所以程序也就报错了。

传统的方式

所以,当你写在代码中写下如下的代码:

<script type="text/javascript" src="http://coolshell.cn/asyncjs/alert.js"></script>

基本上来说,head里的 <script>标签会阻塞后续资源的载入以及整个页面的生成。我专门做了一个示例你可以看看:示例一。 注意:我的alert.js中只有一句话:alert(“hello world”) ,这更容易让你看到javascript是怎么阻塞后面的东西的。 所以,你知道为什么有很多网站把javascript放在网页的最后面了,要么就是动用了window.onload或是docmuemt ready之类的事件。 另外,因为绝大多数的Javascript代码并不需要等页面,所以,我们异步载入的功能。那么我们怎么异步载入呢?

document.write方式

于是,你可能以为document.write()这种方式能够解决不阻塞的方式。你当然会觉得,document.write了 的<script>标签后就可以执行后面的东西去了,这没错。对于在同一个script标签里的Javascript的代码来说,是这样的, 但是对于整个页面来说,这个还是会阻塞。 下面是一段测试代码:

<script type="text/javascript" language="javascript">// <![CDATA[
function loadjs(script_filename) {
        document.write('<' + 'script language="javascript" type="text/javascript"');         document.write(' src="' + script_filename + '">');
        document.write('<'+'/script'+'>');
        alert("loadjs() exit...");
    }

    var script = 'http://coolshell.cn/asyncjs/alert.js';

    loadjs(script);
    alert("loadjs() finished!");
// ]]></script>

<script type="text/javascript" language="javascript">// <![CDATA[
alert("another block");
// ]]></script>

你觉得alert的顺序是什么?你可以在不同的浏览器里试一试。这里的想关的测试页面:示例二

script的defer和async属性

IE自从IE6就支持defer标签,如:

<script type="text/javascript" src="./alert.js" defer="defer">// <![CDATA[

// ]]></script>

对于IE来说,这个标签会让IE并行下载js文件,并且把其执行hold到了整个DOM装载完毕(DOMContentLoaded),多个 defer的<script>在执行时也会按照其出现的顺序来运行。最重要的是<script>被加上defer后,其不会阻塞 后续DOM的的渲染。但是因为这个defer只是IE专用,所以一般用得比较少。

而我们标准的的HTML5也加入了一个异步载入javascript的属性:async,无论你对它赋什么样的值,只要它出现,它就开始异步加载 js文件。但是, async的异步加载会有一个比较严重的问题,那就是它忠实地践行着“载入后马上执行”这条军规,所以,虽然它并不阻塞页面的渲染,但是你也无法控制他执 行的次序和时机。你可以看看这个示例去感受一下

支持 async标签的浏览器是:Firefox3.6+,Chrome 8.0+,Safari 5.0+,IE 10+,Opera还不支持(来自这里)所以这个方法也不是太好。因为并不是所有的浏览器你都能行。

动态创建DOM方式

这种方式可能是用得最多的了。

function loadjs(script_filename) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', script_filename);
script.setAttribute('id', 'coolshell_script_id');

script_id = document.getElementById('coolshell_script_id');
if(script_id){
document.getElementsByTagName('head')[0].removeChild(script_id);
}
document.getElementsByTagName('head')[0].appendChild(script);
}

var script = 'http://coolshell.cn/asyncjs/alert.js';
loadjs(script);

这个方式几乎成了标准的异步载入js文件的方式,这个方式的演示请参看:示例三。这方式还被玩出了JSONP的东东,也就是我可以为script的src指定某个后台的脚本(如PHP),而这个PHP返回一个javascript函数,其参数是一个json的字符串,返回来调用我们的预先定义好的javascript的函数。你可以看一下这个示例:t.js (这个示例是我之前在微博征集的一个异步ajax调用的小例子

按需异步载入js

上面那个DOM方式的例子解决了异步载入Javascript的问题,但是没有解决我们想让他按我们指定的时机运行的问题。所以,我们只需要把上面那个DOM方式绑到某个事件上来就可以了。

比如:

绑在window.load事件上——示例四 

你一定要比较一下示例四和示例三在执行上有什么不同,我在这两个示例中都专门用了个代码高亮的javascript,看看那个代码高亮的的脚本的执行和我的alert.js的执行的情况,你就知道不同了)

window.load = loadjs("http://coolshell.cn/asyncjs/alert.js")

绑在特定的事件上——示例五

<p style="cursor: pointer;" onclick="LoadJS()">Click to load alert.js</p>

这个示例很简单了。当你点击某个DOM元素,才会真正载入我们的alert.js。

更多

但是,绑定在某个特定事件上这个事似乎又过了一点,因为只有在点击的时候才会去真正的下载js,这又会太慢了了。好了,到这里,要抛出我们的终极问题——我们想要异步地把js文件下载到用户的本地,但是不执行,仅当在我们想要执行的时候去执行

要是我们有下面这样的方式就好了:

var script = document.createElement("script");
script.noexecute = true;
script.src = "alert.js";
document.body.appendChild(script);

//后面我们可以这么干
script.execute();

可惜的是,这只是一个美丽的梦想,今天我们的Javascript还比较原始,这个“JS梦”还没有实现呢。

所以,我们的程序员只能使用hack的方式来搞。

有的程序员使用了非标准的script的type来cache javascript。如:

<script type="cache/script" src="./alert.js"></script>

因为”cache/script”,这个东西根本就不能被浏览器解析,所以浏览器也就不能把alert.js当javascript去执行,但是他 又要去下载js文件,所以就可以搞定了。可惜的是,webkit严格符从了HTML的标准——对于这种不认识的东西,直接删除,什么也不干。于是,我们的 梦又破了。

所以,我们需要再hack一下,就像N多年前玩preload图片那样,我们可以动用object标签(也可以动用iframe标签),于是我们有下面这样的代码:

function cachejs(script_filename){
var cache = document.createElement('object');
cache.data = script_filename;
cache.id = "coolshell_script_cache_id";
cache.width = 0;
cache.height = 0;
document.body.appendChild(cache);
}

然后,我们在的最后调用一下这个函数。请参看一下相关的示例:示例六

在Chrome下按 Ctrl+Shit+I,切换到network页,你就可以看到下载了alert.js但是没有执行,然后我们再用示例五的方式,因为浏览器端有缓存了,不会再从服务器上下载alert.js了。所以,就能保证执行速度了。

关于这种preload这种东西你应该不会陌生了。你还可以使用Ajax的方式,如:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'new.js');
xhr.send('');

到这里我就不再多说了,也不给示例了,大家可以自己试试去。

最后再提两个js,一个是ControlJS,一个叫HeadJS,专门用来做异步load javascript文件的。

好了,这是所有的内容了,希望大家看过后能对Javascript的载入和执行,以及相关的技术有个了解。同时,也希望各前端高手不吝赐教!

[转载]Jetty架构解析及应用示例 - windlaughing - 博客园

mikel阅读(1183)

[转载]Jetty架构解析及应用示例 – windlaughing – 博客园.

Jetty 是一个 Web server/servlet container, 支持 SPDY,WebSocketOSGiJMX,JNDIJAAS 。Jetty非常高效而且灵活,Google App Engine 选择了Jetty,而放弃了Tomcat,或是其他的服务器。

Jetty has a slogan, “Don’t deploy your application in Jetty, deploy Jetty in your application.” What this means is that, putting an HTTP module into your application, rather than putting your application into an HTTP server.

Jetty的口号是:“不要把你的程序部署到Jetty里,而是把Jetty部署到你的程序里”,意味着,你可以把Jetty当成程序的一个HTTP模块放到你的程序里。

本文先通过一个简单的HelloWorld示例,展示了java应用中的Jetty是如何启动的;接着详细分析了Jetty的整体架构;最后展示了用Jetty启动一个标准的Java web app。

Hello World 示例

需要的jar包:

jetty-server-8.1.11.v20130520.jar
javax.servlet-3.0.0.v201112011016.jar
jetty-continuation-8.1.11.v20130520.jar
jetty-http-8.1.11.v20130520.jar
jetty-io-8.1.11.v20130520.jar
jetty-util-8.1.11.v20130520.jar

HelloWorldHandler 类:

package edu.shao.jetty.sample;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;

public class HelloWorldHandler extends AbstractHandler {

public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
response.getWriter().println("
<h1>Hello World</h1>
");
}
}

MyServer 类:

package edu.shao.jetty.sample;

import org.eclipse.jetty.server.Server;

public class MyServer {

public static void main(String[] args) throws Exception {
Server server = new Server(8081);
server.setHandler(new HelloWorldHandler());
server.start();
server.join();
}
}

运行main()函数,在浏览器内输入:http://localhost:8081/ 就可以看得结果。

 

Jetty架构

1、整体架构图:

The Jetty Server is the plumbing between a collection of Connectors that accept HTTP connections, and a collection of Handlers that service requests from the connections and produce responses, with the work being done by threads taken from a thread pool.(The concept of a Servlet itself is implemented by a Servlet Handler.  you can build a Jetty server using only connectors and handlers, without using Servlets.)

2、顶层类结构:

受JSR77规范的启发,Jetty的绝大多数的组件(Connector, Handler ,Buffer)都实现了LifeCycle接口。

3、Connectors:

The connectors represent the protocol handlers that accept connections, parse requests and generate responses. The different types of connectors available are based on the protocols, scheduling model and IO APIs used:

1、SocketConnector – for few busy connections or when NIO is not available

2、BlockingChannelConnector – for few busy connections when NIO is available

3、SelectChannelConnector – for many mostly idle connections or asynchronous handling of Ajax requests

  4、SslSocketConnector – SSL without NIO

  5、SslSelectChannelConnector – SSL with non blocking NIO support

  6、AJPConnector – AJP protocol support for connections from apache mod_jk or mod_proxy_ajp

4、Handlers:

  

The Handler is the component that deals with received requests. Three styles of Handler: 

1、Coordinating Handlers – Handlers that route requests to other handlers (eg HandlerCollection, ContextHandlerCollection)
2、Filtering Handlers – Handlers that augment a request and pass it on to other handlers (eg. HandlerWrapper, ContextHandler, SessionHandler)
3、Generating Handlers – Handlers that produce content (eg ResourceHandler and ServletHandler)

重点Handler:

1、The ServletHandler is a Handler that generates content by passing the request to any configured Filters and then to a Servlet mapped by a URI pattern.

2、A WebAppContext combines handlers for security, session and servlets in a single unit that can be configured with a web.xml descriptor.

你可以顺序调用Handler,或者嵌套调用Handler,来处理请求的不同方面。

  

5、web应用

A WebAppContext supports the standardized layout of a web application and configuration of session, security, listeners, filter, servlets and JSP via a web.xml descriptor normally found in the WEB-INF directory of a webapplication.

 

把Jetty“部署”到Web应用中

1、开发时的部署示例:

这种部署方式还有一个诱人的特性:项目启动后,如果某个类没有被加载到内存中,对这个类的修改在下次该类被调用时就会生效,而不用重启动项目;对JSP的修改,任何时候都会在下次被调用时生效,而不用重启项目。这将给开发web应用带来极大的便利。

这是用Maven构件的Java Web App项目,项目结构如下:

  

WebappStart 类:

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

public class WebappStart {

    public static void main(String[] args) throws Exception {
        Server server = new Server(8082);
         
        WebAppContext context = new WebAppContext();
        context.setResourceBase("./src/main/webapp");
        context.setDescriptor("./src/main/webapp/WEB-INF/web.xml");
        context.setContextPath("/test2");
        context.setParentLoaderPriority(true);
 
        server.setHandler(context);
 
        server.start();
        server.join();

    }

}

启动main()函数,整个web项目就启动了。

我们可以体验到,把Jetty嵌入到Web项目中,作为Web Server,十分便利、灵活,并且相比其他服务器软件要高效,是开发Web应用的首选WebServer。

2、用Jetty部署war包

此部分稍后撰写。

[转载]微信公众平台开发(二) 微信公众平台示例代码分析 - David_Tang - 博客园

mikel阅读(1147)

[转载]微信公众平台开发(二) 微信公众平台示例代码分析 – David_Tang – 博客园.

一、摘要

微信公众平台提供了一个简单的php示例代码,在做进一步开发之前,我们有必要将其详细了解一下。

二、获取代码

微信官网:http://mp.weixin.qq.com/mpres/htmledition/res/wx_sample.zip

三、分析代码

完整代码如下:

<!--?php /**   * wechat php test   */ //define your token define("TOKEN", "weixin"); $wechatObj = new wechatCallbackapiTest(); $wechatObj--->valid();

class wechatCallbackapiTest
{
public function valid()
{
$echoStr = $_GET["echostr"];

//valid signature , option
if($this-&gt;checkSignature()){
echo $echoStr;
exit;
}
}

public function responseMsg()
{
//get post data, May be due to the different environments
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

//extract post data
if (!empty($postStr)){

$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$fromUsername = $postObj-&gt;FromUserName;
$toUsername = $postObj-&gt;ToUserName;
$keyword = trim($postObj-&gt;Content);
$time = time();
$textTpl = "
<![CDATA[%s]]>
<![CDATA[%s]]>
%s
<![CDATA[%s]]>
<![CDATA[%s]]>
0
";
if(!empty( $keyword ))
{
$msgType = "text";
$contentStr = "Welcome to wechat world!";
$resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
echo $resultStr;
}else{
echo "Input something...";
}

}else {
echo "";
exit;
}
}

private function checkSignature()
{
$signature = $_GET["signature"];
$timestamp = $_GET["timestamp"];
$nonce = $_GET["nonce"];

$token = TOKEN;
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );

if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
}

?&gt;

3.1 整体分析

原始示例代码大致分为四个部分:

  • 定义TOKEN
  • 声明一个类 wechatCallbackapiTest
  • 创建类wechatCallbackapiTest 的一个实例对象 $wechatObj
  • 调用类的 valid() 方法。

3.2 详细分析

3.2.1 定义TOKEN

define(“TOKEN”, “weixin”);

define 是用来给常量赋值的函数,这句话的意思是赋予“TOKEN”这个常量值为“weixin”。

TOKEN 是用来进行交互安全认证的,开发者可以随意定义,要和公众平台里设置的一样。

3.2.2 声明一个类

class wechatCallbackapiTest{

}

声明一个类 wechatCallbackapiTest,该类中包含有三个方法(函数)。

a. public function valid()

用于申请 成为开发者 时向微信发送验证信息。

b. public function responseMsg()

处理并回复用户发送过来的消息,也是用的最多的一个函数,几乎所有的功能都在这里实现。

responseMsg 函数详解:

$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
接收微信公众平台发送过来的用户消息,该消息数据结构为XML,不是php默认的识别数据类型,因此这里用了$GLOBALS['HTTP_RAW_POST_DATA']来接收,同时赋值给了$postStr

if (!empty($postStr))
判断$postStr是否为空,如果不为空(接收到了数据),就继续执行下面的语句;如果为空,则跳转到与之相对应的else语句。

$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
使用simplexml_load_string() 函数将接收到的XML消息数据载入对象$postObj中。这个严谨的写法后面还得加个判断是否载入成功的条件语句,不过不写也没事。

$fromUsername = $postObj->FromUserName;
将对象$postObj中的发送消息用户的OPENID赋值给$fromUsername变量

$toUsername = $postObj->ToUserName;
将对象$postObj中的公众账号的ID赋值给$toUsername变量

$keyword = trim($postObj->Content);
trim() 函数从字符串的两端删除空白字符和其他预定义字符,这里就可以得到用户输入的关键词

$time = time();
time() 函数返回当前时间的 Unix 时间戳,即自从 Unix 纪元(格林威治时间 1970 年 1 月 1 日 00:00:00)到当前时间的秒数。

$textTpl = "<xml>
       <ToUserName><![CDATA[%s]]></ToUserName>
       <FromUserName><![CDATA[%s]]></FromUserName>
        <CreateTime>%s</CreateTime>
       <MsgType><![CDATA[%s]]></MsgType>
       <Content><![CDATA[%s]]></Content>
       <FuncFlag>0</FuncFlag>
       </xml>";
存放微信输出内容的模板

if(!empty( $keyword ))
判断$keyword是否为空,不为空则继续执行下面的语句;如果为空,则跳转到与之相对应的else语句,即 echo "Input something...";

$msgType = "text";
消息类型是文本类型

$contentStr = "Welcome to wechat world!";
回复的消息内容

$resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
使用sprintf() 函数将格式化的数据写入到变量中去;
$fromUsername, $toUsername, $time, $msgType, $contentStr 分别顺序替换模板里“%s”位置,也即是“$resultStr”这个变量最后实际为:
<xml>
							<ToUserName><![CDATA[$toUsername]]></ToUserName>
							<FromUserName><![CDATA[$fromUsername]]></FromUserName>
							<CreateTime>$time</CreateTime>
							<MsgType><![CDATA[$msgType]]></MsgType>
							<Content><![CDATA[$contentStr]]></Content>
							<FuncFlag>0</FuncFlag>      //位0x0001被标志时,星标刚收到的消息。
							</xml>
echo $resultStr;     //把回复的消息输出

c. private function checkSignature()

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请求原样返回echostr参数内容,则接入生效,否则接入失败。

signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。

加密/校验流程:
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

3.2.3 创建实例对象

$wechatObj = new wechatCallbackapiTest();

3.2.4 调用类方法验证

$wechatObj->valid();

调用类的valid()方法执行接口验证,接口设置成功后将其注释掉。

四、总结

以上是对微信官方示例代码的一个分析,有解释不对的地方,还请高手指出。另外,该代码只是官方给出的简单示例代码,如果要做复杂的开发,还是要求开发者按照严谨的开发模式改写该段代码,会在后续教程中娓娓道来。

五、参考

微信官方公众平台API文档:http://mp.weixin.qq.com/wiki/index.php

[转载]All Tabs Restorer – 一键显示所有的标签页[Firefox] - 小众软件

mikel阅读(900)

[转载]All Tabs Restorer – 一键显示所有的标签页[Firefox] – 小众软件.

All Tabs Restorer 是款 Firefox 扩展,用来一键显示所有标签页,支持快捷键 Ctrl+Shift+Tab 及标签页搜索。@Appinn

All Tabs Restorer   一键显示所有的标签页[Firefox][图] | 小众软件

很简单实用的功能,适合标签控,除了 Ctrl+Shift+Tab 快捷键,还可以在工具栏添加按钮。

All Tabs Restorer   一键显示所有的标签页[Firefox][图] | 小众软件 https://addons.mozilla.org/…/all-tabs-restorer/