web优化之-Asp.net MVC js、css动态合并 动态压缩 (2)

来源:互联网 发布:怎么加入淘宝联盟会 编辑:程序博客网 时间:2024/05/02 04:20

在前一篇文章web优化之-Asp.net MVC js、css动态合并 动态压缩中的js和css的路径都是Scripts/jquery-1.5.1.js,Scripts/jquery.validate.js这在http请求时路径比较长,为此我们可以改用[Scripts/jquery-1.5.1,jquery.validate]这种格式。同时为了防止文件更新后客户端无法刷新的问题我们加了version标记

修改后的代码:

CombineFiles:

    using System;    using System.Collections.Generic;    using System.Linq;    using System.Web;    using System.Text;    using System.IO;    using Yahoo.Yui.Compressor;    /// <summary>    /// Summary description for CombineFiles    /// </summary>    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;            string version = string.Empty;            if (allkeys.Contains("version"))            {                version = request.QueryString["version"].Trim().ToLower();            }            if (!allkeys.Contains("href") || !allkeys.Contains("type") || !allkeys.Contains("compress"))            {                response.Write("请求格式不正确,正确格式是type=....&href=....&compress=...\r\n");                response.Write("type只能是js或则css,compress只能是true或则false,href则是请求的文件,多个文件已逗号分隔\r\n");                response.Write("示例如下:\r\n type=js&compress=true&href=[Scripts/jquery-1.5.1,jquery.validate][Scripts/MicrosoftAjax]");            }            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 && !String.IsNullOrEmpty(cacheItem.Version) && !string.IsNullOrEmpty(version))                {                    if (cacheItem.Version != version)                    {                        cacheItem = null;                    }                }                if (cacheItem == null)                {                    #region 合并压缩文件                    /*合并文件*/                    string href = context.Request.QueryString["href"].Trim();                    string content = string.Empty;                    string[] files = GetFilePath(href);                    StringBuilder sb = new StringBuilder();                    foreach (string fileName in files)                    {                        string filePath = context.Server.MapPath(fileName + "." + fileType);                        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);                            content = (new JavaScriptCompressor()).Compress(content);                        }                        else if (fileType.Equals("css"))                        {                           // content = CssCompressor.Compress(content);                            content = (new CssCompressor()).Compress(content);                        }                    }                    /*压缩文件*/                    #endregion                    cacheItem = new CacheItem() { Content = content, Expires = DateTime.Now.AddHours(1), Version = version };                    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);                }            }  //合并文件结束        }        string[] GetFilePath(string filesrc)        {            List<string> result = new List<string>();            string[] files = filesrc.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);            foreach (string file in files)            {                string[] srcs = file.Split(new char[] { ',' });                string first = srcs[0];                int index = first.LastIndexOf("/");                string prefx = first.Substring(0, index);                result.Add(first);                if (srcs.Length > 1)                {                    srcs = srcs.Where((x, i) => { return i > 0; }).Select(x => prefx + "/" + x).ToArray();                    result.AddRange(srcs);                }            }            return result.ToArray();        }        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 string Version { set; get; }        }        public bool IsReusable        {            get            {                return false;            }        }    }

CombineResFile:

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;    using System.IO;    public enum ResOrderType    {        Highest = 1,        High = 2,        Normal = 3,        Low = 4,        Lowest = 5    }    public enum ResourceType    {        /// <summary>        /// CSS        /// </summary>        StyleSheet = 0,        /// <summary>        /// JS        /// </summary>        Script = 1    }    public static class CombineResFile    {        class ResourceInfo        {            public string Url { set; get; }            public string Group { set; get; }            public ResOrderType Order { set; get; }        }        const string conAppendFileKey = "AppendFileKey";        const string conRemoveFileKey = "RemoveFileKey";        const string conRemoveGroupKey = "RemoveGroupKey";        /// <summary>        /// 合并路径        /// </summary>        /// <param name="paths"></param>        /// <returns></returns>        /// <summary>        /// 添加资源文件        /// </summary>        /// <param name="htmlHelper"></param>        /// <param name="resType">资源类型</param>        /// <param name="url">文件的路径</param>        /// <param name="group">文件的分组名称</param>        /// <param name="order">文件同组中的优先级,默认:Normal</param>        public static void AppendResFile(this HtmlHelper htmlHelper, ResourceType resType, string url, string group = "", ResOrderType order = ResOrderType.Normal)        {            AppendResFile(htmlHelper, resType, GetFilePath(url), group, order);        }        /// <summary>        /// 添加资源文件        /// </summary>        /// <param name="htmlHelper"></param>        /// <param name="resType">资源类型</param>        /// <param name="urls">文件的路径列表,如“channel/fanbuxie/urlcommon”,不支[]限定符</param>        /// <param name="group">文件的分组名称</param>        /// <param name="order">文件同组中的优先级,默认:Normal</param>        public static void AppendResFile(this HtmlHelper htmlHelper, ResourceType resType, string[] urls, string group = "", ResOrderType order = ResOrderType.Normal)        {            Dictionary<string, ResourceInfo> resFiles = null;            var urlArray = urls.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray<string>();            if (urlArray == null || urlArray.Length == 0)            {                return;            }            string key = string.Format("{0}_{1}", resType.ToString(), conAppendFileKey);            if (htmlHelper.ViewContext.HttpContext.Items.Contains(key))            {                resFiles = htmlHelper.ViewContext.HttpContext.Items[key] as Dictionary<string, ResourceInfo>;            }            else            {                resFiles = new Dictionary<string, ResourceInfo>();                htmlHelper.ViewContext.HttpContext.Items.Add(key, resFiles);            }            for (int i = 0; i < urlArray.Length; i++)            {                if (resFiles.Keys.Contains(urlArray[i]))                {                    resFiles[urlArray[i]].Group = group;                    resFiles[urlArray[i]].Order = order;                }                else                {                    resFiles.Add(urlArray[i], new ResourceInfo() { Url = urlArray[i], Group = group, Order = order });                }            }            htmlHelper.ViewContext.HttpContext.Items[key] = resFiles;        }        /// <summary>        /// 移除资源文件        /// </summary>        /// <param name="resType">资源类型</param>        /// <param name="htmlHelper"></param>        /// <param name="urls">移除文件,可以为空或则null </param>        /// <param name="group">移除文件所在的组可以为null</param>        public static void RemoveResFile(this HtmlHelper htmlHelper, ResourceType resType, string url, string group = "")        {            RemoveResFile(htmlHelper, resType, GetFilePath(url), group);        }        /// <summary>        /// 移除资源文件        /// </summary>        /// <param name="resType">资源类型</param>        /// <param name="htmlHelper"></param>        /// <param name="urls">移除文件列表,可以为空或则null </param>        /// <param name="group">移除文件所在的组可以为null</param>        public static void RemoveResFile(this HtmlHelper htmlHelper, ResourceType resType, string[] urls, string group = "")        {            var urlArray = urls.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray<string>();            #region 按照js的地址移除            if (urlArray != null && urlArray.Length > 0)            {                List<string> removeFileKeys = null;                string key = string.Format("{0}_{1}", resType.ToString(), conRemoveFileKey);                if (htmlHelper.ViewContext.HttpContext.Items.Contains(key))                {                    removeFileKeys = htmlHelper.ViewContext.HttpContext.Items[key] as List<string>;                }                else                {                    removeFileKeys = new List<string>();                    htmlHelper.ViewContext.HttpContext.Items.Add(key, removeFileKeys);                }                for (int i = 0; i < urlArray.Length; i++)                {                    var url = urlArray[i].Trim().ToLower();                    if (!removeFileKeys.Contains(url))                    {                        removeFileKeys.Add(url);                    }                }            }            #endregion            #region  按照js的group移除            if (!string.IsNullOrEmpty(group))            {                List<string> removeGroupKeys = null;                string keyGroup = string.Format("{0}_{1}", resType.ToString(), conRemoveGroupKey);                if (htmlHelper.ViewContext.HttpContext.Items.Contains(keyGroup))                {                    removeGroupKeys = htmlHelper.ViewContext.HttpContext.Items[keyGroup] as List<string>;                }                else                {                    removeGroupKeys = new List<string>();                    htmlHelper.ViewContext.HttpContext.Items.Add(keyGroup, removeGroupKeys);                }                if (!removeGroupKeys.Contains(group))                {                    removeGroupKeys.Add(group);                }            }            #endregion        }        /// <summary>        /// 输出js        /// </summary>        /// <param name="htmlHelper"></param>        /// <returns></returns>        public static MvcHtmlString RenderResFile(this HtmlHelper htmlHelper, ResourceType resType)        {            string keyAppend = string.Format("{0}_{1}", resType.ToString(), conAppendFileKey);            string keyRemove = string.Format("{0}_{1}", resType.ToString(), conRemoveGroupKey);            string keyRemoveGroup = string.Format("{0}_{1}", resType.ToString(), conRemoveGroupKey);            Dictionary<string, ResourceInfo> resFiles = null;            StringBuilder content = new StringBuilder();            if (htmlHelper.ViewContext.HttpContext.Items.Contains(keyAppend))            {                resFiles = htmlHelper.ViewContext.HttpContext.Items[keyAppend] as Dictionary<string, ResourceInfo>;                List<string> removeFileKey = new List<string>();                if (htmlHelper.ViewContext.HttpContext.Items.Contains(keyRemove))                {                    removeFileKey = htmlHelper.ViewContext.HttpContext.Items[keyRemove] as List<string>;                }                List<string> removeGroupKey = new List<string>();                if (htmlHelper.ViewContext.HttpContext.Items.Contains(keyRemoveGroup))                {                    removeGroupKey = htmlHelper.ViewContext.HttpContext.Items[keyRemoveGroup] as List<string>;                }                List<ResourceInfo> files = resFiles.Select(x => x.Value)                    .Where(x => !removeFileKey.Contains(x.Url) && !removeGroupKey.Contains(x.Group))                    .ToList<ResourceInfo>();                IEnumerable<IGrouping<string, ResourceInfo>> jsGroupFiles = files.OrderByDescending(x => x.Group).GroupBy(x => x.Group);                foreach (IGrouping<string, ResourceInfo> item in jsGroupFiles)                {                    var resPath = CombinePath(item.ToArray());                    string version = "0";                    //获取版本号                    version = GetVersion(item.ToArray(), resType);                    switch (resType)                    {                        case ResourceType.StyleSheet:                            string cssformat = "<link charset=\"utf-8\" rel=\"stylesheet\" type=\"text/css\" href=\"/CombineFiles.ashx?type=js&compress=false&href={0}&version={1}\">";                            content.Append(string.Format(cssformat, resPath, version));                            break;                        case ResourceType.Script:                            string jsformat = "<script type=\"text/javascript\" src=\"/CombineFiles.ashx?type=js&compress=true&href={0}&version={1} \"></script>";                            content.Append(string.Format(jsformat, resPath, version));                            break;                    }                }            }//end if            return new MvcHtmlString(content.ToString());        }        static string CombinePath(ResourceInfo[] items)        {            var all = items.OrderBy(x => x.Order).GroupBy(x => x.Order).ToArray();            StringBuilder sb = new StringBuilder();            foreach (var item in all)            {                int order = 1;                var files = item.Select(x =>                {                    int lastIndex = x.Url.LastIndexOf('/');                    string prefix = x.Url.Substring(0, lastIndex);                    string fileName = x.Url.Substring(lastIndex + 1);                    return new { Prfx = prefix, FileName = fileName, FileOrder = order++ };                }).OrderBy(x => x.FileOrder);                var keysgroup = files.GroupBy(x => x.Prfx).ToArray();                foreach (var key in keysgroup)                {                    var list = files.Where(x => x.Prfx.Equals(key.Key)).ToArray();                    sb.Append("[" + list[0].Prfx + "/" + list[0].FileName);                    string jsprfx = list[0].Prfx;                    for (int i = 1; i < list.Length; i++)                    {                        sb.Append("," + list[i].FileName);                    }                    sb.Append("]");                }            }            return sb.ToString();        }        static string[] GetFilePath(string filesrc)        {            List<string> result = new List<string>();            string[] files = filesrc.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);            foreach (string file in files)            {                string[] srcs = file.Split(new char[] { ',' });                string first = srcs[0];                int index = first.LastIndexOf("/");                string prefx = first.Substring(0, index);                result.Add(first);                if (srcs.Length > 1)                {                    srcs = srcs.Where((x, i) => { return i > 0; }).Select(x => prefx + "/" + x).ToArray();                    result.AddRange(srcs);                }            }            return result.ToArray();        }        static string GetVersion(ResourceInfo[] items, ResourceType restype)        {            StringBuilder sb = new StringBuilder();            string ext = ".js";            if (restype == ResourceType.StyleSheet)            {                ext = ".css";            }            foreach (ResourceInfo item in items)            {                string filename = item.Url + ext;                string filepath = HttpContext.Current.Server.MapPath(filename);                FileInfo file = new FileInfo(filepath);                sb.Append(file.LastWriteTime.ToString("yyyyMMddHHmmss"));            }            string version = sb.ToString().GetHashCode().ToString();            version = version.Replace("-", string.Empty);            ///Read web config                        ///            return version;        }    }}


结果如图:


注意Yahoo.Yui.Compressor版本不同,调用方式也不同.

如果是在MVC4项目里,这里可以不用Yahoo.Yui.Compressor来压缩文件,可以用WebGrease.1.1.0\lib\WebGrease.dll中的Minifier类来实现压缩.代码如下:

CodeSettings codeSettings = new CodeSettings
{
EvalTreatment = EvalTreatment.MakeImmediateSafe,
PreserveImportantComments = false
};

content= (new Microsoft.Ajax.Utilities.Minifier()).MinifyJavaScript(content,codeSettings);

--------------------------------------------------------------------

CssSettings setting = new CssSettings() { CommentMode=CssComment.None };
content = (new Microsoft.Ajax.Utilities.Minifier()).MinifyStyleSheet(content,setting);