上次写了两篇有关js的合并压缩如
同样我们还需要写一个扩展方法:
web优化之-mvc js动态合并 动态压缩 去掉js重复引用 js缓存 js延迟加载
web优化之-asp.net js延迟加载 js动态合并 js动态压缩
看了一下的访问两比较大,现在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。欢迎大家拍砖。