当开启这个过滤器后,最终生成的HTML代码将会被压缩一下,在流量很大的网站中,能减少带宽成本就减少一点,何乐而不为?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using Mkt.Common;
namespace Mkt.Mvc.Filter
{
public class HtmlFilter : ActionFilterAttribute
{
#region = IsAvailable =
private bool _isavailable = true;
public bool IsAvailable
{
get { return _isavailable; }
set { _isavailable = value; }
}
#endregion
public HtmlFilter() { }
public HtmlFilter(bool isAvailable)
{
_isavailable = isAvailable;
}
public string SetGray(string text)
{
text = Common.HtmlHelper.Compress(text);
if (DateTime.Now.Month == 4 && DateTime.Now.Day == 21)
{
text = Regex.Replace(text, "</head>", @"<style type=""text/css"">html {filter:gray;}</style></head>
", RegexOptions.IgnoreCase);
}
return text;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
if (!IsAvailable) return;
#if DEBUG
return;
#endif
filterContext.RequestContext.HttpContext.Response.Filter = new HtmlStreamFilter(filterContext.RequestContext.HttpContext.Response.Filter, filterContext.RequestContext.HttpContext.Response.ContentEncoding, SetGray);
}
}
}
compress代码:
/// <summary>
/// 压缩html代码
/// </summary>
/// <param name="text">The text.</param>
/// <returns></returns>
public static string Compress(string text)
{
text = Regex.Replace(text, @"<!--\S*?-->", string.Empty);
text = Regex.Replace(text, @"^\s+|\s+{1}quot;, string.Empty);
text = Regex.Replace(text, "\n", " ");
text = Regex.Replace(text, @">\s+?<", "><");
text = Regex.Replace(text, @"\s{2,}", " ");
text = Regex.Replace(text, " {2,}", @"\s");
text = Regex.Replace(text, @"\s{2,}", @"\s");
return text;
}
其中的SetGray是4.21哀悼日变灰设置.
配合CompressFilter,效果更佳~
之前露掉了HtmlStreamFilter的源码,如下:
public class HtmlStreamFilter : Stream
{
Stream responseStream;
long position;
StringBuilder responseHtml;
#region = CurrentEncoding =
private Encoding _currentencoding;
public Encoding CurrentEncoding
{
get { return _currentencoding; }
set { _currentencoding = value; }
}
#endregion
Func<string, string> _func;
public HtmlStreamFilter(Stream inputStream, Encoding enc, Func<string, string> func
)
{
responseStream = inputStream;
_currentencoding = enc;
_func = func;
responseHtml = new StringBuilder();
}
#region Filter overrides
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Close()
{
responseStream.Close();
}
public override void Flush()
{
responseStream.Flush();
}
public override long Length
{
get { return 0; }
}
public override long Position
{
get { return position; }
set { position = value; }
}
public override long Seek(long offset, SeekOrigin origin)
{
return responseStream.Seek(offset, origin);
}
public override void SetLength(long length)
{
responseStream.SetLength(length);
}
public override int Read(byte[] buffer, int offset, int count)
{
return responseStream.Read(buffer, offset, count);
}
#endregion
#region Dirty work
public override void Write(byte[] buffer, int offset, int count)
{
string strBuffer = CurrentEncoding.GetString(buffer, offset, count);
#region =如果不是HTML文档,不作处理=
var bof = new Regex("<html", RegexOptions.IgnoreCase);
if (!bof.IsMatch(responseHtml.ToString()))
{
responseStream.Write(buffer, offset, count);
return;
}
#endregion
// ---------------------------------
// Wait for the closing </html> tag
// ---------------------------------
Regex eof = new Regex("</html>", RegexOptions.IgnoreCase);
if (!eof.IsMatch(strBuffer))
{
responseHtml.Append(strBuffer);
}
else
{
responseHtml.Append(strBuffer);
string finalHtml = responseHtml.ToString();
finalHtml = _func(finalHtml);
// Transform the response and write it back out
byte[] data = CurrentEncoding.GetBytes(finalHtml);
responseStream.Write(data, 0, data.Length);
}
}
#endregion
}
第二种方法,使用.net内部类实现:
public class HtmlFilter : ActionFilterAttribute
{
#region = IsAvailable =
private bool _isavailable = true;
public bool IsAvailable
{
get { return _isavailable; }
set { _isavailable = value; }
}
#endregion
private TextWriter _originalWriter;
private static readonly MethodInfo SwitchWriterMethod = typeof(HttpResponse).GetMethod("SwitchWriter", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
public HtmlFilter() { }
public HtmlFilter(bool isAvailable)
{
_isavailable = isAvailable;
}
public string SetGray(string text)
{
text = Common.HtmlHelper.Compress(text);
if (DateTime.Now.Month == 4 && DateTime.Now.Day == 21)
{
text = Regex.Replace(text, "</head>", @"<style type=""text/css"">html {filter:gray;}</style></head>
", RegexOptions.IgnoreCase);
}
return text;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsAvailable) return;
#if DEBUG
return;
#endif
_originalWriter = (TextWriter)SwitchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { new HtmlTextWriter(new StringWriter()) });
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
if (!IsAvailable) return;
#if DEBUG
return;
#endif
HtmlTextWriter cacheWriter = (HtmlTextWriter)SwitchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { _originalWriter });
string textWritten = cacheWriter.InnerWriter.ToString();
if (filterContext.HttpContext.Response.ContentType == "text/html")
{
textWritten = Compress(textWritten).ToString();
}
filterContext.HttpContext.Response.Write(textWritten);
//filterContext.RequestContext.HttpContext.Response.Filter = new HtmlStreamFilter(filterContext.RequestContext.HttpContext.Response.Filter, filterContext.RequestContext.HttpContext.Response.ContentEncoding, SetGray);
}
private static StringBuilder Compress(string text)
{
StringBuilder str = new StringBuilder();
StringBuilder strlink = new StringBuilder();
var s = new char[] { '\f', '\n', '\r', '\t', '\v' };
Func<int, object> P = c => null;
Func<int, object> Ptag = c => null; //标签处理机
Func<int, object> Pcomment = c => null; //注释
#region - 总处理机 -
Func<int, object> state = P = i =>
{
char c = text[i];
if (c == '<') //碰到<交个Ptag处理机
{
if (i + 4 < text.Length)
{
if (text.Substring(i + 1, 3) == "!--") //交个注释处理机
{
return Pcomment;
}
}
str.Append(c);
return Ptag;
}
else if (s.Contains(c) == true) { return P; }
else if (c == ' ')
{
if (i + 1 < text.Length)
{
if (s.Union(new char[] { ' ', '<' }).Contains(text[i + 1]) == false)
{
str.Append(c);
}
}
return P;
}
else
{
str.Append(c);
return P;
}
};
#endregion
#region - Tag处理机 -
Ptag = i =>
{
char c = text[i];
if (c == '>') //交还给p
{
str.Append(c);
return P;
}
else if (s.Contains(c) == true) { return Ptag; }
else if (c == ' ')
{
if (i + 1 < text.Length)
{
if (new char[] { ' ', '/', '=', '>' }.Contains(text[i + 1]) == false)
{
str.Append(c);
}
}
return Ptag;
}
else
{
str.Append(c);
return Ptag;
}
};
#endregion
#region - 注释处理机 -
Pcomment = i =>
{
char c = text[i];
if (c == '>' && text.Substring(i - 2, 3) == "-->")
{
return P;
}
else
{
return Pcomment;
}
};
#endregion
for (int index = 0; index < text.Length; index++)
{
state = (Func<int, object>)state(index);
}
return str;
}
}
此方法中新的compress方法使用的 GoodSpeed的成果.