HttpCombiner也不记得是谁写的了,功能是把多个js文件或css文件合并到一块,压缩一下一起发给客户端来优化网站。
用法是这样的:
<
script
type
="text/javascript"
src
="/Part/Handle/HttpCombiner.ashx?t=js&s=a.js,b.js,dialog/c.js,dialog/d.js"
></
script
>
但这样又不利于找错,所以在中间又加了一个方法,可随时控制是如上引用还是,如下一般引用:
<
script
type
="text/javascript"
src
="/RES/JS/a.js"
></
script
>
<
script
type
="text/javascript"
src
="/RES/JS/b.js"
></
script
>
<
script
type
="text/javascript"
src
="/RES/JS/
dialog
/c.js
"
></
script
>
<
script
type
="text/javascript"
src
="/RES/JS/dialog/d.js"
></
script
>
修改后引用文件时:
<%= HttpCombiner.Requires(true,"js","a.js", "b.js", "dialog/c.js", "dialog/d.js")%>
第一个参数来控制合并 。
一般处理程序cs源码:
using System;
using System.Web;
using System.Net;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Web.Caching;
public
class HttpCombiner : IHttpHandler
{
#region Config
private
const
bool DO_GZIP =
true;
private
readonly
static TimeSpan CACHE_DURATION = TimeSpan.FromDays(
30);
private
const
string JSPathPre =
"
~/RES/JS/
";
private
const
string CSSPathPre =
"
~/RES/CSS/
";
private
const
string CombinerUrl =
"
/Part/Handle/HttpCombiner.ashx
";
//
些handler路径
private
const
string JSAbsPathPre =
"
/RES/JS/
";
private
const
string CSSAbsPathPre =
"
/RES/CSS/
";
#endregion
#region Requires 默认combin
public
static
string Requires(
bool combin,
string type,
params
string[] files)
{
if (combin)
{
if (type ==
"
js
")
{
return
string.Format(
"
<script type=\"text/javascript\" src=\"{0}?t=js&s={1}\"></script>
", CombinerUrl,
string.Join(
"
,
", files));
}
else
if (type ==
"
css
")
{
return
string.Format(
"
<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}?t=css&s={1}\" />
", CombinerUrl,
string.Join(
"
,
", files));
}
else
{
return
string.Empty;
}
}
else
{
if (type ==
"
js
")
{
StringBuilder sb =
new StringBuilder();
foreach (
var file
in files)
{
sb.AppendFormat(
"
<script type=\"text/javascript\" src=\"{0}{1}\"></script>
", JSAbsPathPre, file);
sb.AppendLine();
}
return sb.ToString();
}
else
if (type ==
"
css
")
{
StringBuilder sb =
new StringBuilder();
foreach (
var file
in files)
{
sb.AppendFormat(
"
<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}{1}\" />
", CSSAbsPathPre, file);
sb.AppendLine();
}
return sb.ToString();
}
else
{
return
string.Empty;
}
}
}
public
static
string Requires(
string type,
params
string[] files)
{
return Requires(
true, type, files);
}
#endregion
#region Process
public
void ProcessRequest(HttpContext context)
{
HttpRequest request = context.Request;
string setName = request[
"
s
"] ??
string.Empty;
string contentType = request[
"
t
"] ??
string.Empty;
if (
string.IsNullOrEmpty(contentType))
{
contentType =
"
type/javascript
";
}
else
{
if (contentType ==
"
js
")
{
contentType =
"
type/javascript
";
}
else
if (contentType ==
"
css
")
{
contentType =
"
text/css
";
}
else
{
contentType =
"
text/plain
";
}
}
bool isCompressed = DO_GZIP && CanGZip(context.Request);
UTF8Encoding encoding =
new UTF8Encoding(
false);
if (!WriteFromCache(context, setName, isCompressed, contentType))
{
System.Collections.Generic.List<
string> dependencyFiles =
new System.Collections.Generic.List<
string>();
using (MemoryStream memoryStream =
new MemoryStream(
5000))
{
using (Stream writer = isCompressed ?
(Stream)(
new GZipStream(memoryStream, CompressionMode.Compress)) :
memoryStream)
{
string[] fileNames = setName.Split(
new
char[] {
'
,
' }, StringSplitOptions.RemoveEmptyEntries);
foreach (
string fileName
in fileNames)
{
byte[] fileBytes = GetFileBytes(contentType, context, fileName.Trim(), encoding, dependencyFiles);
writer.Write(fileBytes,
0, fileBytes.Length);
}
writer.Close();
}
byte[] responseBytes = memoryStream.ToArray();
context.Cache.Insert(GetCacheKey(setName, isCompressed), responseBytes,
new CacheDependency(dependencyFiles.ToArray()), System.Web.Caching.Cache.NoAbsoluteExpiration, CACHE_DURATION);
WriteBytes(responseBytes, context, isCompressed, contentType);
}
}
}
private
static
byte[] GetFileBytes(
string contentType, HttpContext context,
string virtualPath, Encoding encoding, System.Collections.Generic.List<
string> depencesFile)
{
if (virtualPath.StartsWith(
"
http://
", StringComparison.InvariantCultureIgnoreCase))
{
using (WebClient client =
new WebClient())
{
return client.DownloadData(virtualPath);
}
}
else
{
if (!virtualPath.StartsWith(
"
~/
", StringComparison.InvariantCultureIgnoreCase))
{
if (contentType ==
"
text/css
")
{
virtualPath = CSSPathPre + virtualPath;
}
else
if (contentType ==
"
type/javascript
")
{
virtualPath = JSPathPre + virtualPath;
}
}
string physicalPath = context.Server.MapPath(virtualPath);
depencesFile.Add(physicalPath);
byte[] bytes = File.ReadAllBytes(physicalPath);
return bytes;
}
}
private
static
bool WriteFromCache(HttpContext context,
string setName,
bool isCompressed,
string contentType)
{
byte[] responseBytes = context.Cache[GetCacheKey(setName, isCompressed)]
as
byte[];
if (
null == responseBytes ||
0 == responseBytes.Length)
return
false;
WriteBytes(responseBytes, context, isCompressed, contentType);
return
true;
}
private
static
void WriteBytes(
byte[] bytes, HttpContext context,
bool isCompressed,
string contentType)
{
HttpResponse 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
static
bool CanGZip(HttpRequest request)
{
string acceptEncoding = request.Headers[
"
Accept-Encoding
"];
if (!
string.IsNullOrEmpty(acceptEncoding) && (acceptEncoding.Contains(
"
gzip
") || acceptEncoding.Contains(
"
deflate
")))
return
true;
return
false;
}
private
static
string GetCacheKey(
string setName,
bool isCompressed)
{
return
"
HttpCombiner.
" + setName +
"
.
" + isCompressed;
}
public
bool IsReusable
{
get
{
return
true;
}
}
#endregion
}