ASP.NET 打包多CSS或JS文件以加快页面加载速度的Handler
ASP.NET 打包多CSS或JS文件以加快页面加载速度的Handler,
使用<link type="text/css" rel="Stylesheet" href="HttpCombiner.ashx?" />,具体的参数请参考程序中的介绍。附件
using System; using System.IO; using System.IO.Compression; using System.Net; using System.Text; using System.Web; using System.Web.Services; namespace SLTech.DST.Web.Application { /// <summary> /// Summary description for $codebehindclassname$ /// </summary> [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class HttpCombiner : IHttpHandler { private const bool DO_GZIP = true; private readonly static TimeSpan CACHE_DURATION = TimeSpan.FromDays(30); private const string DEFAULT_CSS = "decision-support-toolkit.css,controls/tab.css,controls/extension-button.css,"; public void ProcessRequest(HttpContext context) { var request = context.Request; // Read setName, contentType and version. //All are required. They are used as cache key var setName = request["s"] ?? string.Empty; var contentType = request["t"] ?? string.Empty; var version = request["v"] ?? string.Empty; var files = request["f"] ?? string.Empty; files = DEFAULT_CSS + files; // Decide if browser supports compressed response var isCompressed = DO_GZIP && this.CanGZip(context.Request); // Response is written as UTF8 encoding. var encoding = new UTF8Encoding(false); // If the set has already been cached, write the response directly from // cache. Otherwise generate the response and cache it if (!this.WriteFromCache(context, setName, version, isCompressed, contentType)) { using (var memoryStream = new MemoryStream(5000)) { // Decide regular stream or GZipStream based on whether the response // can be cached or not using (var writer = isCompressed ? (Stream)(new GZipStream(memoryStream, CompressionMode.Compress)) : memoryStream) { // Load the files defined and process each file var fileNames = files.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string fileName in fileNames) { var fileBytes = this.GetFileBytes(context,"css/"+fileName.Trim(), encoding); writer.Write(fileBytes, 0, fileBytes.Length); } writer.Close(); } var responseBytes = memoryStream.ToArray(); context.Cache.Insert(GetCacheKey(setName, version, isCompressed), responseBytes, null, System.Web.Caching.Cache.NoAbsoluteExpiration, CACHE_DURATION); // Generate the response this.WriteBytes(responseBytes, context, isCompressed, contentType); } } } private byte[] GetFileBytes(HttpContext context, string virtualPath, Encoding encoding) { if (virtualPath.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)) { using (var client = new WebClient()) { return client.DownloadData(virtualPath); } } else { var physicalPath = context.Server.MapPath(virtualPath); var bytes = File.ReadAllBytes(physicalPath); return bytes; } } private bool WriteFromCache(HttpContext context, string setName, string version, bool isCompressed, string contentType) { var responseBytes = context.Cache[GetCacheKey(setName, version, isCompressed)] as byte[]; if (null == responseBytes || 0 == responseBytes.Length) return false; this.WriteBytes(responseBytes, context, isCompressed, contentType); return true; } private void WriteBytes(byte[] bytes, HttpContext context, bool isCompressed, string contentType) { var response = context.Response; response.AppendHeader("Content-Length", bytes.Length.ToString()); response.ContentType = contentType; if (isCompressed) response.AppendHeader("Content-Encoding", "gzip"); context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.SetExpires(DateTime.Now.Add(CACHE_DURATION)); context.Response.Cache.SetMaxAge(CACHE_DURATION); context.Response.Cache.AppendCacheExtension("must-revalidate, proxy-revalidate"); response.OutputStream.Write(bytes, 0, bytes.Length); response.Flush(); } private bool CanGZip(HttpRequest request) { var acceptEncoding = request.Headers["Accept-Encoding"]; return (!string.IsNullOrEmpty(acceptEncoding) && (acceptEncoding.Contains("gzip") || acceptEncoding.Contains("deflate"))); } private string GetCacheKey(string setName, string version, bool isCompressed) { return "HttpCombiner." + setName + "." + version + "." + isCompressed; } public bool IsReusable { get { return false; } } } }
"唯有高屋建瓴,方可水到渠成"