web优化之-Asp.net MVC js、css动态合并 动态压缩
上次写了两篇有关js的合并压缩如
web优化之-m js动态合并 动态压缩 去掉js重复引用 js缓存 js延迟加载
web优化之-.net js延迟加载 js动态合并 js动态压缩
看了一下的访问两比较大,现在js和css的合并压缩整理一下。
首先还是需要一个handler来处理文件的合并、压缩、缓存,.js和css的压缩我们采用的是yahoo.yui.compressor,所以需要引用yahoo.yui.compressor.dll文件
代码如下:
[csharp] view plaincopyprint?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;
}
}
}
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;
}
}
}
同样我们还需要写一个扩展方法:
[csharp] view plaincopyprint?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
}
}
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和cs