您的位置:首页 > Web前端 > CSS

合并多个css或js请求 来加快页面加载速度

2015-09-01 00:00 639 查看
摘要: 合并多个CSS或js请求、压缩、缓存从而加快页面加载速度

使用ashx文件合并、压缩、缓存多个CSS、js或url成一个Response来减少请求次数进而提高页面加载速度。

实践证明把js和css分成多个小文件比放在一个整个大的js或css文件来说更容易维护,但是多个js或css文件会影响网站的性能。但把css或js文件分成多个小文件页面加载时,浏览器对每个js或css文件都要建立一个请求,每次请求都会有一定的延迟,假如每次网络平均延迟100ms 那么7个css或js文件就要消耗 100*7=700ms



为了减少延迟,减少页面加载时间我们可以使用cdn ,但也可以把多个文件通过一次请求来得到。



引用js文件时可以写成下面形式:



<script type="text/javascript" src ="../services/HttpCombinerHandler.ashx?k=MasterJS&t=text/javascript&v=0"></script>

其中“k=MasterJS”表示webconfig中配置的键值,“t=text/javascript”表示要返回的文件类型,“v=0”表示版本号,因为读取的文件被缓存了(k对应的值和v对应的值作为主键进行缓存的) 所以在调试阶段每次修改了对应的js或css文件就要对版本号加一这样才能得到最新修改后的文件。

在webconfig中配置成这样:

<appSettings>
<add key="MasterJS" value="../Scripts/jquery-1.10.2.min.js,
../Scripts/jquery-ui.min.js,
../Scripts/MasterPage.js"/>
</appSettings>

目的是通过写一个HttpCombinerHandler.ashx 在后台把 这些js文件取到一次返回前台。

using System.Web;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Web.Caching;
using System.Configuration;
using System.Net;
using Controller;

/// <summary>
/// 把多个文件打包传到前台 比如多个js或css文件打包成一个请求发送到前台
/// 作者:XingSQ
/// 日期:2015-08-26
/// </summary>
public class HttpCombinerHandler : IHttpHandler {

private readonly static TimeSpan CACHE_DURATION = TimeSpan.FromDays(30);

public void ProcessRequest (HttpContext context) {

HttpRequest request = context.Request;
//
string setName = request["k"] ?? string.Empty;     //key键
string contentType = request["t"] ?? string.Empty; //内容类型
string version = request["v"] ?? string.Empty;     //版本

//判断是否支持gzip压缩
bool isCompressed = this.CanGZip(request); ;

UTF8Encoding encoding = new UTF8Encoding(false);
//判断请求内容是否在缓存里,在的话直接从cache里读取写入response,否则生成response 并缓存
if (!this.WriteFromCache(context, setName, version, isCompressed, contentType))
{
//创建其存储区支持内存的流
using (MemoryStream memoryStream = new MemoryStream(5000))
{
//根据response是否支持压缩缓存来指定使用GZipStream或MemoryStream
using (Stream writer = isCompressed ? (Stream)(new GZipStream(memoryStream, CompressionMode.Compress)) : memoryStream)
{
string setDefinition = ConfigurationManager.AppSettings[setName];
string[] fileNames = setDefinition.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
//分别取得指定文件并写入当前流
foreach (string fileName in fileNames)
{
byte[] fileBytes = this.GetFileBytes(context, fileName);
writer.Write(fileBytes, 0, fileBytes.Length);
}
writer.Close();
}

byte[] responseBytes = memoryStream.ToArray();
//缓存
CacheManager.Set(setName + version, responseBytes, CACHE_DURATION);
this.WriteBytes(responseBytes, context, isCompressed, contentType);
}
}
}

public bool IsReusable {
get {
return false;
}
}

/// <summary>
/// 判断请求内容是否在cache 是的话直接读取发送
/// </summary>
/// <param name="context"></param>
/// <param name="name"></param>
/// <param name="version"></param>
/// <param name="isComparessed"></param>
/// <returns></returns>
private bool WriteFromCache(HttpContext context, string name, string version, bool isComparessed,string contentType)
{
string cachekey = name + version;
if (CacheManager.Exist(cachekey))
{
this.WriteBytes((byte[])CacheManager.Get(cachekey), context, isComparessed, contentType);
return true;
}
else
{
return false;
}
}

/// <summary>
/// 判断浏览器知否支持gzip压缩
/// </summary>
/// <param name="request"></param>
/// <returns>true:支持 false:不支持</returns>
private bool CanGZip(HttpRequest request)
{
string AcceptEncoding = request.Headers["Accept-Encoding"]; // “gzip,deflate”
if (string.IsNullOrEmpty(AcceptEncoding))
{
return false;
}
return AcceptEncoding.Contains("gzip");
}

/// <summary>
/// 取得指定文件内容
/// </summary>
/// <param name="context"></param>
/// <param name="virtualPath">路径</param>
/// <param name="coder"></param>
/// <returns></returns>
private byte[] GetFileBytes(HttpContext context, string virtualPath/*, Encoder coder*/)
{
if(virtualPath.StartsWith("http://",StringComparison.InvariantCultureIgnoreCase))
{
using( WebClient client = new WebClient())
{
return client.DownloadData(virtualPath);
}
}
else
{
string physicalPath = context.Server.MapPath(virtualPath);
byte[] bytes = File.ReadAllBytes(physicalPath);
//TODO 转码
return bytes;
}
}

/// <summary>
/// 写入输出流,并发送到前台
/// </summary>
/// <param name="bytes"></param>
/// <param name="context"></param>
/// <param name="isCompressed">是否支持压缩</param>
/// <param name="contentType">内容类型 比如:text/css、text/javascript</param>
private void WriteBytes(byte[] bytes, HttpContext context,bool isCompressed,string contentType)
{
HttpResponse response = context.Response;

response.AppendHeader("Conten-Length",bytes.Length.ToString());
response.ContentType = contentType;
if(isCompressed)
{
response.AppendHeader("Content-Encoding","gzip");
}

response.Cache.SetCacheability(HttpCacheability.Public);
response.Cache.SetExpires(DateTime.Now.Add(CACHE_DURATION));
response.Cache.SetMaxAge(CACHE_DURATION);
response.Cache.AppendCacheExtension("");

response.OutputStream.Write(bytes,0,bytes.Length);
response.Flush();
}

}

//缓存管理类
public class CacheManager
{
/// <summary>
/// 初始化服务器缓存
/// </summary>
public static System.Web.Caching.Cache m_cache = HttpRuntime.Cache;
public static TimeSpan m_span = new TimeSpan(0,30,0);//30分钟有效

/// <summary>
/// 添加缓存信息
/// </summary>
/// <param name="key">键</param>
/// <param name="value">要缓存的信息</param>
public static void Set(string sKey,object oValue)
{
m_cache.Insert(sKey,oValue, null, System.Web.Caching.Cache.NoAbsoluteExpiration,m_span);
}

/// <summary>
/// 添加缓存,并设置失效时间
/// </summary>
/// <param name="sKey">键</param>
/// <param name="oValue"></param>
/// <param name="timespan">失效时间</param>
public static void Set(string sKey, object oValue, TimeSpan timespan)
{
m_cache.Insert(sKey, oValue, null, System.Web.Caching.Cache.NoAbsoluteExpiration, timespan);
}

/// <summary>
/// 返回指定的缓存信息
/// </summary>
/// <param name="key">键</param>
/// <returns>缓存信息</returns>
public static object Get(string key)
{
return m_cache[key];
}

/// <summary>
/// 删除指定键的缓存
/// </summary>
/// <param name="key">键</param>
/// <returns>缓存信息</returns>
public static bool Remove(string key)
{
return !(null == m_cache.Remove(key));
}

/// <summary>
/// 判定是否存在知道键的缓存信息
/// </summary>
/// <param name="key">键</param>
/// <returns>是否存在</returns>
public static bool Exist(string key)
{
return !(null == m_cache[key]);
}
}

主要过程是 HttpCombinerHandler 通过请求中的k对应的参数“MasterJS”在webconfig中取到要取得哪些文件及其路径。查看缓存,如果缓存中有对应文件那么直接回写response进行输出。如果缓存中没有的话,则取得文件并根据浏览器知否支持gzip压缩进行处理,处理后进行缓存并回写response进行输出。

参考:http://www.codeproject.com/Articles/28909/HTTP-Handler-to-Combine-Multiple-Files-Cache-and-D
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息