有过ftp下载文件经验的人都有体验,单独下载一个100M的文件比分开下载100个1M的文件速度快很多,这是因为下载需要解析域名,建立连接等额外开销的时间,因此下载100个文件就要做100次这种额外的开销。因此如果把这个100个文件整合成一个文件下载速度会快很多。

基于上述的方法,可以对HTML中的JS文件也做类似的处理,无论js文件多少个,通过配置文件,把N个js文件合并到一个文件下载。实现手段通过httpHandler。具体代码如下:

web.config文件中:

 

 
  
  1. <httpHandlers> 
  2.       <add  verb="*" path="MergedJS.js" type="MergeJS.GetJS"/> 
  3. </httpHandlers> 

 

只对MergedJS.js的请求文件作合并,具体请求到哪个文件,有querystring参数给定,比如本例中使用MergedJS.js?jsid=myjs这种方式。对于其他类型的js文件,则按照IIS默认的请求方式处理。

 

 
  
  1.   <appSettings>   
  2.     <add key="myjs" value="jquery-1.4.1-vsdoc.js,   
  3. jquery-ui-1.8.16.custom.min.js,   
  4. jquery-1.4.1.js,   
  5. jquery-1.7.min.js,   
  6. jquery-1.5.2.min.js,   
  7. jquery-1.4.1.min.js,   
  8. jquery.maskedinput-1.3.js,   
  9. json2.min.js,   
  10. jquery.hotkeys-0.0.3.js,   
  11. jquery.loadmask.min.js,   
  12. My.js"/>   
  13.   </appSettings> 
  14.  

 

上述配置,主要用来指定myjs使用哪些文件来合并。

MergeJS.GetJS 这个HttpHandler主要用来处理js。此处涉及到一个缓存的问题,通常情况下,js文件会默认缓存在本地浏览器缓存中,但是对于MergedJS.js这种通过httpHandler所处理的,被视为动态内容,并不会缓存在浏览器中,而是每次都会从服务器端获取最新的内容。因此可以通过编码,强制使用浏览器缓存。

由于反复读取js所做IO操作也是性能瓶颈,因此在global中首先将所有的js预读存在dictionary中,用的时候直接从dictionary中输出,避免了IO操作。代码能够判断js文件是否做了修改,如果修改了,dictionary中的值也会得到更新。

Handler处理部分的代码如下:

 

 
  
  1. /// <summary>  
  2.    /// GetJS 的摘要说明  
  3.    /// </summary>  
  4.    public class GetJS : IHttpHandler  
  5.    {  
  6.  
  7.        public void ProcessRequest(HttpContext context)  
  8.        {  
  9.            context.Response.ContentType = "application/x-javascript";//表示是javascript文件类型  
  10.            string jsid = context.Request.Params["jsid"].ToString();  
  11.  
  12.            //获取所有需要Merge的文件  
  13.            string[] jsfiles = System.Configuration.ConfigurationManager.AppSettings[jsid].ToString().Replace("\r""").Replace("\n""").Split(',');  
  14.  
  15.            //记录文件的最后修改时间,取最新修改的那个文件的时间。  
  16.            DateTime lastChangeTime = new DateTime();  
  17.            StringBuilder sb = new StringBuilder();  
  18.            foreach (var js in jsfiles)  
  19.            {  
  20.                if (Global.dic_jsfiles.ContainsKey(js))  
  21.                {  
  22.                    DateTime tmplastDateTime = new FileInfo(Global.dic_jsfiles[js].filefullName).LastWriteTime;  
  23.  
  24.                    tmplastDateTime = DateTime.Parse(tmplastDateTime.ToString("yyyy-MM-dd HH:mm:ss"));//去除毫秒信息。  
  25.  
  26.                    if (tmplastDateTime > Global.dic_jsfiles[js].lastModifiedTime)//如果最后修改时间更改,更新dic_jsfiles内容  
  27.                    {  
  28.                        Global.dic_jsfiles[js].content = Common.ReadFile(Global.dic_jsfiles[js].filefullName);//读取新的文件  
  29.                        Global.dic_jsfiles[js].lastModifiedTime = tmplastDateTime;  
  30.                    }  
  31.                      
  32.                }  
  33.                else//如果不存在,则读取该文件  
  34.                {   
  35.                    string filename = context.Request.PhysicalApplicationPath + "Scripts\\" + js.Trim();  
  36.                    Global.dic_jsfiles.Add(filename,new jsFileInfo() {  
  37.                        filefullName = filename,content=Common.ReadFile(filename),lastModifiedTime=new FileInfo(filename).LastWriteTime  
  38.                      
  39.                    });  
  40.                    Common.ReadFile(Global.dic_jsfiles[js].filefullName);  
  41.                }  
  42.  
  43.                sb.Append(Global.dic_jsfiles[js].content + ";");  
  44.                 
  45.            }  
  46.  
  47.            //文件最后修改时间  
  48.            lastChangeTime = Global.dic_jsfiles.Max(m => m.Value.lastModifiedTime);  
  49.  
  50.            DateTime If_Modified_Since = new DateTime();  
  51.            if (context.Request.Headers["If-Modified-Since"] == null || context.Request.Headers["If-Modified-Since"] == "")  
  52.            {  
  53.                If_Modified_Since = new DateTime(1900, 1, 1);//如果读取的请求头中If-Modified-Since没有值,就给它一个默认值为19000101  
  54.            }  
  55.            else 
  56.            {  
  57.                If_Modified_Since = DateTime.Parse(context.Request.Headers["If-Modified-Since"]);  
  58.            }  
  59.            if (If_Modified_Since >= lastChangeTime)//如果客户端请求头中的时间最后一次修改时间,比真实的最后修改时间还新,则直接返回304代码  
  60.            {  
  61.                context.Response.StatusCode = 304;//返回304代码,使其读取缓存  
  62.                context.Response.End();  
  63.            }  
  64.            else//否则  
  65.            {  
  66.                //显示内容  
  67.                context.Response.AddHeader("last-modified", lastChangeTime.ToString());  
  68.            }  
  69.  
  70.            context.Response.Write(sb.ToString());  
  71.        }  
  72.  
  73.        private string ReadFile(string filename)  
  74.        {  
  75.            using (StreamReader sr = new StreamReader(filename, System.Text.UTF8Encoding.UTF8))  
  76.            {  
  77.                return sr.ReadToEnd();  
  78.            }  
  79.        }  
  80.  
  81.        public bool IsReusable  
  82.        {  
  83.            get 
  84.            {  
  85.                return false;  
  86.            }  
  87.        }  
  88.    } 

 

效果如下:使用组合下载的情况,js的下载耗费时间73ms。

clipboard

但是分开下载的话就比较耗时。

clipboard[1]

当然,并不是合并为一个文件,下载的速度就一定快,具体的下载速度和策略还受到浏览器的影响,有的浏览器同时开的请求线程多,也会影响下载速度。但是总体来说,如果文件数多的话,下载单一文件总会快点的。