看了一下的访问两比较大,现在js和css的合并压缩整理一下。
首先还是需要一个handler来处理文件的合并、压缩、缓存.js和css的压缩我们采用的是Yahoo.Yui.Compressor,所以需要引用Yahoo.Yui.Compressor.dll文件
代码如下:
public class CombineFiles : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/javascript"; HttpRequest request = context.Request; HttpResponse response = context.Response; string[] allkeys = request.QueryString.AllKeys; if (!allkeys.Contains("href") || !allkeys.Contains("type") || !allkeys.Contains("compress")) { response.Write("请求格式不正确,正确格式是type=....&href=....&compress=..."); response.Write("type只能是js或则css,compress只能是true或则false,href则是请求的文件,多个文件已逗号分隔"); } else { string cacheKey = request.Url.Query; #region /*确定合并文件类型*/ string fileType = request.QueryString["type"].Trim().ToLower(); string contenType = string.Empty; if (fileType.Equals("js")) { contenType = "text/javascript"; } else if (fileType.Equals("css")) { contenType = "text/css"; } /*确定合并文件类型*/ #endregion CacheItem cacheItem = HttpRuntime.Cache.Get(cacheKey) as CacheItem;//服务端缓存 if (cacheItem == null) { #region 合并压缩文件 /*合并文件*/ string href = context.Request.QueryString["href"].Trim(); string content = string.Empty; string[] files = href.Split(new string[] { ",", "," }, StringSplitOptions.RemoveEmptyEntries); StringBuilder sb = new StringBuilder(); foreach (string fileName in files) { string filePath = context.Server.MapPath(fileName); if (File.Exists(filePath)) { string readstr = File.ReadAllText(filePath, Encoding.UTF8); sb.Append(readstr); //content = JavaScriptCompressor.Compress(content); //response.Write(content); } else { sb.AppendLine("\r\n未找到源文件" + filePath + "\r\n"); } } content = sb.ToString(); /*合并文件*/ /*压缩文件*/ string compressStr = request.QueryString["compress"].Trim(); bool iscompress = bool.Parse(compressStr); if (iscompress) { if (fileType.Equals("js")) { content = JavaScriptCompressor.Compress(content); } else if (fileType.Equals("css")) { content = CssCompressor.Compress(content); } } /*压缩文件*/ #endregion cacheItem = new CacheItem() { Content = content, Expires = DateTime.Now.AddHours(1) }; HttpRuntime.Cache.Insert(cacheKey, cacheItem, null, cacheItem.Expires, TimeSpan.Zero); } response.ContentType = contenType; if (request.Headers["If-Modified-Since"] != null && TimeSpan.FromTicks(cacheItem.Expires.Ticks - DateTime.Parse(request.Headers["If-Modified-Since"]).Ticks).Seconds < 100) { response.StatusCode = 304; // response.Headers.Add("Content-Encoding", "gzip"); response.StatusDescription = "Not Modified"; } else { response.Write(cacheItem.Content); SetClientCaching(response, DateTime.Now); } } //合并文件结束 } private void SetClientCaching(HttpResponse response, DateTime lastModified) { response.Cache.SetETag(lastModified.Ticks.ToString()); response.Cache.SetLastModified(lastModified); //public 以指定响应能由客户端和共享(代理)缓存进行缓存。 response.Cache.SetCacheability(HttpCacheability.Public); //是允许文档在被视为陈旧之前存在的最长绝对时间。 response.Cache.SetMaxAge(new TimeSpan(7, 0, 0, 0)); //将缓存过期从绝对时间设置为可调时间 response.Cache.SetSlidingExpiration(true); } class CacheItem { public string Content { set; get; } public DateTime Expires { set; get; } } public bool IsReusable { get { return false; } } }
同样我们还需要写一个扩展方法:
namespace System.Web.Mvc.Html { using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Text; using System.Collections; public static class CombineJSCss { class AppendInfo { public string Url { set; get; } public int Group { set; get; } public int Order { set; get; } } const string jsAppendFileKey = "JSAppendFileKey"; const string jsRemoveFileKey = "JSRemoveFileKey"; const string jsRemoveGroupKey = "JSRemoveGroupKey"; const string cssAppendFileKey = "CSSAppendFileKey"; const string cssRemoveFileKey = "CSSRemoveFileKey"; const string cssRemoveGroupKey = "CSSRemoveGroupKey"; #region private Method private static void AppendFiles(HtmlHelper htmlHelper, string url, int group, int order, string appendFileKey) { Dictionary<string, AppendInfo> files = null; url = url.ToLower().Trim(); if (string.IsNullOrEmpty(url)) return; IDictionary Items = htmlHelper.ViewContext.HttpContext.Items; if (Items.Contains(appendFileKey)) { files = Items[appendFileKey] as Dictionary<string, AppendInfo>; } else { files = new Dictionary<string, AppendInfo>(); Items.Add(appendFileKey, files); } if (files.Keys.Contains(url)) { files[url].Group = group; files[url].Order = order; } else { files.Add(url, new AppendInfo() { Url = url, Group = group, Order = order }); } Items[appendFileKey] = files; } private static void RemoveFiles(HtmlHelper htmlHelper, string url, int? group, string removeFilekey, string removeGroupKey) { IDictionary Items = htmlHelper.ViewContext.HttpContext.Items; if (!string.IsNullOrEmpty(url)) { url = url.Trim().ToLower(); List<string> removeFileKeys = null; if (Items.Contains(removeFilekey)) { removeFileKeys = Items[removeFilekey] as List<string>; } else { removeFileKeys = new List<string>(); Items.Add(removeFilekey, removeFileKeys); } if (!removeFileKeys.Contains(url)) { removeFileKeys.Add(url); } /*按照js的地址移除*/ } if (group.HasValue) { List<int> removeGroupKeys = null; if (Items.Contains(removeGroupKey)) { removeGroupKeys = Items[removeGroupKey] as List<int>; } else { removeGroupKeys = new List<int>(); Items.Add(removeGroupKey, removeGroupKeys); } if (!removeGroupKeys.Contains(group.Value)) { removeGroupKeys.Add(group.Value); } /*按照js的group移除*/ } } private static MvcHtmlString RenderFiles(HtmlHelper htmlHelper, string appendFileKey, string removeFilekey, string removeGroupKey, Func<string, string> fun) { Dictionary<string, AppendInfo> appendfiles = null; StringBuilder content = new StringBuilder(); IDictionary Items = htmlHelper.ViewContext.HttpContext.Items; if (Items.Contains(appendFileKey)) { appendfiles = Items[appendFileKey] as Dictionary<string, AppendInfo>; List<string> removeFileKeys = new List<string>(); if (Items.Contains(removeFilekey)) { removeFileKeys = Items[removeFilekey] as List<string>; } List<int> removeGroupKeys = new List<int>(); if (Items.Contains(removeGroupKey)) { removeGroupKeys = Items[removeGroupKey] as List<int>; } List<AppendInfo> files = appendfiles.Select(x => x.Value) .Where(x => !removeFileKeys.Contains(x.Url) && !removeGroupKeys.Contains(x.Group)) .ToList<AppendInfo>(); IEnumerable<IGrouping<int, AppendInfo>> groupFiles = files.OrderBy(x => x.Group).GroupBy(x => x.Group); foreach (IGrouping<int, AppendInfo> item in groupFiles) { string filepath = item.OrderBy(x => x.Order).Select(x => x.Url).ToArray().Aggregate((x, y) => x + "," + y); content.Append(fun(filepath)); } }//end if return new MvcHtmlString(content.ToString()); } #endregion #region Public method public static void AppendJsFille(this HtmlHelper htmlHelper, string url, int group = 1, int order = 1) { AppendFiles(htmlHelper, url, group, order, jsAppendFileKey); } public static void RemoveJsFille(this HtmlHelper htmlHelper, string url, int? group=null) { RemoveFiles(htmlHelper, url, group, jsRemoveFileKey, jsRemoveGroupKey); } public static MvcHtmlString RenderJsFille(this HtmlHelper htmlHelper) { return RenderFiles(htmlHelper, jsAppendFileKey, jsRemoveFileKey, jsRemoveGroupKey, x => { string jsformat = "<script type=\"text/javascript\" src=\"/CombineFiles.ashx?type=js&compress=true&href={0}\"></script>"; return string.Format(jsformat, x); }); } public static void AppendCssFille(this HtmlHelper htmlHelper, string url, int group = 1, int order = 1) { AppendFiles(htmlHelper, url, group, order, cssAppendFileKey); } public static void RemoveCssFille(this HtmlHelper htmlHelper, string url, int? group=null) { RemoveFiles(htmlHelper, url, group, cssRemoveFileKey, cssRemoveGroupKey); } public static MvcHtmlString RenderCssFille(this HtmlHelper htmlHelper) { return RenderFiles(htmlHelper, cssAppendFileKey, cssRemoveFileKey, cssRemoveGroupKey, x => { string cssformat = "<link charset=\"utf-8\" rel=\"stylesheet\" type=\"text/css\" href=\"/CombineFiles.ashx?type=js&compress=false&href={0}\">"; return string.Format(cssformat, x); }); } #endregion } }
来看看我们的调用吧,先看看3个view :Index.cshtml、Demo.cshtml、Test.cshtml
那么再来看看_Layout.cshtml吧:
运行的结果如图,请注意js和css各文件的顺序:
再来确认缓存吧
本例代码简单,可能还有一些bug。欢迎大家拍砖。