来源:http://www.oksvn.com/Article/Detail-90.shtml
本文主要讲述ASP.NET网站开发者,给网站增加GZIP压缩模块,提高网页传输速度,由此增加页面显示速度。
什么是网页GZIP压缩?
这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来.一般对纯文本内容可压缩到原大小的40%.这样传输就快了,效果就是你点击网址后会很快的显示出来.当然这也会增加服务器的负载.
一般服务器中都安装有这个功能模块的.
CompressionModule类,主要是开启GZIP压缩功能。
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.IO.Compression;
namespace Compression
{
public class CompressionModule : IHttpModule
{
void IHttpModule.Dispose()
{ }
void IHttpModule.Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(context_PostReleaseRequestState);
}
void context_PostReleaseRequestState(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
if (app.Context.CurrentHandler is System.Web.UI.Page && app.Request["HTTP_X_MICROSOFTAJAX"] == null)
{
if (IsEncodingAccepted(GZIP))
{
app.Response.Filter = new GZipStream(app.Response.Filter, CompressionMode.Compress);
SetEncoding(GZIP);
}
else if (IsEncodingAccepted(DEFLATE))
{
app.Response.Filter = new DeflateStream(app.Response.Filter, CompressionMode.Compress);
SetEncoding(DEFLATE);
}
}
}
private const string GZIP = "gzip";
private const string DEFLATE = "deflate";
private static bool IsEncodingAccepted(string encoding)
{
HttpContext context = HttpContext.Current;
return context.Request.Headers["Accept-encoding"] != null && context.Request.Headers["Accept-encoding"].Contains(encoding);
}
private static void SetEncoding(string encoding)
{
HttpContext.Current.Response.AppendHeader("Content-encoding", encoding);
}
}
}
PageBase类,是为了让继承PageBase的页面的head里出现的JS,CSS文件,通过r.aspx输出,由此来启用gzip压缩。
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
namespace Compression
{
public class PageBase : Page
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
CompressJs();
CompressCss();
}
public virtual void CompressJs()
{
foreach (Control control in Page.Header.Controls)
{
LiteralControl c = control as LiteralControl;
if (c != null && c.Text.Trim().StartsWith("<script", StringComparison.OrdinalIgnoreCase) && !c.Text.Contains("http://"))
{
string text = c.Text.Trim();
int i = text.IndexOf("src=\"");
c.Text = text.Insert(i + 5, "/r.aspx?name=");
}
}
}
public virtual void CompressCss()
{
foreach (Control control in Page.Header.Controls)
{
HtmlControl c = control as HtmlControl;
if (c != null && c.Attributes["type"] != null && c.Attributes["type"].Equals("text/css", StringComparison.OrdinalIgnoreCase))
{
if (!c.Attributes["href"].StartsWith("http://"))
{
c.Attributes["href"] = "/r.aspx?name=" + c.Attributes["href"];
c.EnableViewState = false;
}
}
}
}
}
}
r.aspx后台代码,读取对应的JS,CSS文件,并且缓存到服务器内存上,提高脚本文件和样式文件的读取速度。
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
namespace SvnHost
{
public partial class r : Compression.PageBase
{
protected void Page_Load(object sender, EventArgs e)
{
string name = Request.QueryString["name"];
if (name != null && (name.EndsWith(".js", StringComparison.OrdinalIgnoreCase) || name.EndsWith(".css", StringComparison.OrdinalIgnoreCase)))
{
string text = (string)Cache[name.ToLower()];
string ContentType = SetContentType(name);
if (text == null)
{
using (StreamReader sr = new StreamReader(Server.MapPath(name)))
{
text = sr.ReadToEnd();
sr.Close();
}
if (ContentType == "text/javascript")
{
text = StripWhitespaceJs(text);
}
else if (ContentType == "text/css")
{
text = StripWhitespaceCss(text);
}
Cache.Add(name, text, new System.Web.Caching.CacheDependency(Server.MapPath(name)), DateTime.MaxValue, TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, null);
}
Response.Clear();
SetHeaders(name, ContentType);
Response.Write(text);
Response.End();
}
}
private string SetContentType(string name)
{
string ContentType = "text/css";
if (name.EndsWith(".js", StringComparison.OrdinalIgnoreCase))
{
ContentType = "text/javascript";
}
else if (name.EndsWith(".css", StringComparison.OrdinalIgnoreCase))
{
ContentType = "text/css";
}
return ContentType;
}
private void SetHeaders(string file, string ContentType)
{
Response.ContentType = ContentType;
Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
DateTime date = DateTime.Now;
if (Cache[file + "date"] != null)
date = (DateTime)Cache[file + "date"];
string etag = "\"" + date.GetHashCode() + "\"";
string incomingEtag = Request.Headers["If-None-Match"];
Response.Cache.SetExpires(DateTime.Now.ToUniversalTime().AddDays(7));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetMaxAge(new TimeSpan(7, 0, 0, 0));
Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
Response.Cache.SetETag(etag);
if (String.Compare(incomingEtag, etag) == 0)
{
Response.StatusCode = (int)HttpStatusCode.NotModified;
Response.End();
}
}
private string StripWhitespaceJs(string body)
{
string[] lines = body.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
StringBuilder sb = new StringBuilder();
foreach (string line in lines)
{
string s = line.Trim();
if (s.Length > 0 && !s.StartsWith("//"))
sb.AppendLine(s.Trim());
}
body = sb.ToString();
body = Regex.Replace(body, @"^[\s]+|[ \f\r\t\v]+$", String.Empty);
body = Regex.Replace(body, @"([+-])\n\1", "$1 $1");
body = Regex.Replace(body, @"([^+-][+-])\n", "$1");
body = Regex.Replace(body, @"([^+]) ?(\+)", "$1$2");
body = Regex.Replace(body, @"(\+) ?([^+])", "$1$2");
body = Regex.Replace(body, @"([^-]) ?(\-)", "$1$2");
body = Regex.Replace(body, @"(\-) ?([^-])", "$1$2");
body = Regex.Replace(body, @"\n([{}()[\],<>/*%&|^!~?:=.;+-])", "$1");
body = Regex.Replace(body, @"(\W(if|while|for)\([^{]*?\))\n", "$1");
body = Regex.Replace(body, @"(\W(if|while|for)\([^{]*?\))((if|while|for)\([^{]*?\))\n", "$1$3");
body = Regex.Replace(body, @"([;}]else)\n", "$1 ");
body = Regex.Replace(body, @"(?<=[>])\s{2,}(?=[<])|(?<=[>])\s{2,}(?= )|(?<=&ndsp;)\s{2,}(?=[<])", String.Empty);
return body;
}
private string StripWhitespaceCss(string body)
{
body = body.Replace(" ", String.Empty);
body = body.Replace(Environment.NewLine, String.Empty);
body = body.Replace("\t", string.Empty);
body = body.Replace(" {", "{");
body = body.Replace(" :", ":");
body = body.Replace(": ", ":");
body = body.Replace(", ", ",");
body = body.Replace("; ", ";");
body = body.Replace(";}", "}");
//body = Regex.Replace(body, @"/\*[^\*]*\*+([^/\*]*\*+)*/", "$1");
body = Regex.Replace(body, @"(?<=[>])\s{2,}(?=[<])|(?<=[>])\s{2,}(?= )|(?<=&ndsp;)\s{2,}(?=[<])", String.Empty);
return body;
}
}
}
text/html; charset=utf-8 : 1143 bytes, gzip compressed to 962 bytes ( 15.8 % saving )
text/css; charset=utf-8 : 4435 bytes, gzip compressed to 1487 bytes ( 66.5 % saving )
text/javascript; charset=utf-8 : 125592 bytes, gzip compressed to 32646 bytes ( 74 % saving )
可以看到压缩比例高达:74%,还不包括发送以前替换的空格等不可见字符,另外由于放到内存中缓存了,所以性能提升理论上说应该是非常的可观。