ActionFilterAttribute之HtmlFilter,压缩HTML代码

当开启这个过滤器后,最终生成的HTML代码将会被压缩一下,在流量很大的网站中,能减少带宽成本就减少一点,何乐而不为?

[csharp] view plaincopy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Text.RegularExpressions;  
  6. using System.Web.Mvc;  
  7. using Mkt.Common;  
  8.   
  9. namespace Mkt.Mvc.Filter  
  10. {  
  11.     public class HtmlFilter : ActionFilterAttribute  
  12.     {  
  13.  
  14.         #region = IsAvailable =  
  15.   
  16.         private bool _isavailable = true;  
  17.   
  18.         public bool IsAvailable  
  19.         {  
  20.             get { return _isavailable; }  
  21.             set { _isavailable = value; }  
  22.         }  
  23.  
  24.         #endregion  
  25.         public HtmlFilter() { }  
  26.         public HtmlFilter(bool isAvailable)  
  27.         {  
  28.             _isavailable = isAvailable;  
  29.         }  
  30.         public string SetGray(string text)  
  31.         {  
  32.             text = Common.HtmlHelper.Compress(text);  
  33.             if (DateTime.Now.Month == 4 && DateTime.Now.Day == 21)  
  34.             {  
  35.                 text = Regex.Replace(text, "</head>", @"<style type=""text/css"">html {filter:gray;}</style></head>  
  36. ", RegexOptions.IgnoreCase);  
  37.             }  
  38.             return text;  
  39.         }  
  40.         public override void OnActionExecuting(ActionExecutingContext filterContext)  
  41.         {  
  42.             base.OnActionExecuting(filterContext);  
  43.         }  
  44.         public override void OnResultExecuted(ResultExecutedContext filterContext)  
  45.         {  
  46.             base.OnResultExecuted(filterContext);  
  47.             if (!IsAvailable) return;  
  48. #if DEBUG  
  49.             return;  
  50. #endif  
  51.             filterContext.RequestContext.HttpContext.Response.Filter = new HtmlStreamFilter(filterContext.RequestContext.HttpContext.Response.Filter, filterContext.RequestContext.HttpContext.Response.ContentEncoding, SetGray);  
  52.         }  
  53.     }  
  54. }  

compress代码:

[csharp] view plaincopy
  1. /// <summary>  
  2.         /// 压缩html代码  
  3.         /// </summary>  
  4.         /// <param name="text">The text.</param>  
  5.         /// <returns></returns>  
  6.         public static string Compress(string text)  
  7.         {  
  8.             text = Regex.Replace(text, @"<!--\S*?-->", string.Empty);  
  9.             text = Regex.Replace(text, @"^\s+|\s+{1}quot;, string.Empty);  
  10.             text = Regex.Replace(text, "\n", " ");  
  11.             text = Regex.Replace(text, @">\s+?<", "><");  
  12.             text = Regex.Replace(text, @"\s{2,}", " ");  
  13.             text = Regex.Replace(text, " {2,}", @"\s");  
  14.             text = Regex.Replace(text, @"\s{2,}", @"\s");  
  15.           
  16.             return text;  
  17.         }  


其中的SetGray是4.21哀悼日变灰设置.

配合CompressFilter,效果更佳~


之前露掉了HtmlStreamFilter的源码,如下:

[csharp] view plaincopy
  1. public class HtmlStreamFilter : Stream  
  2.     {  
  3.         Stream responseStream;  
  4.         long position;  
  5.         StringBuilder responseHtml;  
  6.         #region = CurrentEncoding =  
  7.   
  8.         private Encoding _currentencoding;  
  9.   
  10.         public Encoding CurrentEncoding  
  11.         {  
  12.             get { return _currentencoding; }  
  13.             set { _currentencoding = value; }  
  14.         }  
  15.  
  16.         #endregion  
  17.         Func<string, string> _func;  
  18.         public HtmlStreamFilter(Stream inputStream, Encoding enc, Func<string, string> func  
  19. )  
  20.         {  
  21.             responseStream = inputStream;  
  22.             _currentencoding = enc;  
  23.             _func = func;  
  24.             responseHtml = new StringBuilder();  
  25.         }  
  26.  
  27.         #region Filter overrides  
  28.         public override bool CanRead  
  29.         {  
  30.             get { return true; }  
  31.         }  
  32.   
  33.         public override bool CanSeek  
  34.         {  
  35.             get { return true; }  
  36.         }  
  37.   
  38.         public override bool CanWrite  
  39.         {  
  40.             get { return true; }  
  41.         }  
  42.   
  43.         public override void Close()  
  44.         {  
  45.             responseStream.Close();  
  46.         }  
  47.   
  48.         public override void Flush()  
  49.         {  
  50.             responseStream.Flush();  
  51.         }  
  52.   
  53.         public override long Length  
  54.         {  
  55.             get { return 0; }  
  56.         }  
  57.   
  58.         public override long Position  
  59.         {  
  60.             get { return position; }  
  61.             set { position = value; }  
  62.         }  
  63.   
  64.         public override long Seek(long offset, SeekOrigin origin)  
  65.         {  
  66.             return responseStream.Seek(offset, origin);  
  67.         }  
  68.   
  69.         public override void SetLength(long length)  
  70.         {  
  71.             responseStream.SetLength(length);  
  72.         }  
  73.   
  74.         public override int Read(byte[] buffer, int offset, int count)  
  75.         {  
  76.             return responseStream.Read(buffer, offset, count);  
  77.         }  
  78.         #endregion  
  79.  
  80.         #region Dirty work  
  81.         public override void Write(byte[] buffer, int offset, int count)  
  82.         {  
  83.             string strBuffer = CurrentEncoding.GetString(buffer, offset, count);  
  84.             #region =如果不是HTML文档,不作处理=  
  85.   
  86.             var bof = new Regex("<html", RegexOptions.IgnoreCase);  
  87.             if (!bof.IsMatch(responseHtml.ToString()))  
  88.             {  
  89.                 responseStream.Write(buffer, offset, count);  
  90.                 return;  
  91.             }  
  92.             #endregion  
  93.             // ---------------------------------  
  94.             // Wait for the closing </html> tag  
  95.             // ---------------------------------  
  96.             Regex eof = new Regex("</html>", RegexOptions.IgnoreCase);  
  97.   
  98.             if (!eof.IsMatch(strBuffer))  
  99.             {  
  100.                 responseHtml.Append(strBuffer);  
  101.             }  
  102.             else  
  103.             {  
  104.                 responseHtml.Append(strBuffer);  
  105.                 string finalHtml = responseHtml.ToString();  
  106.   
  107.                 finalHtml = _func(finalHtml);  
  108.   
  109.                 // Transform the response and write it back out  
  110.   
  111.                 byte[] data = CurrentEncoding.GetBytes(finalHtml);  
  112.   
  113.                 responseStream.Write(data, 0, data.Length);  
  114.             }  
  115.         }  
  116.         #endregion  
  117.   
  118.     }  

第二种方法,使用.net内部类实现:

[csharp] view plaincopy
  1. public class HtmlFilter : ActionFilterAttribute  
  2.     {  
  3.  
  4.         #region = IsAvailable =  
  5.   
  6.         private bool _isavailable = true;  
  7.   
  8.         public bool IsAvailable  
  9.         {  
  10.             get { return _isavailable; }  
  11.             set { _isavailable = value; }  
  12.         }  
  13.  
  14.         #endregion  
  15.         private TextWriter _originalWriter;  
  16.         private static readonly MethodInfo SwitchWriterMethod = typeof(HttpResponse).GetMethod("SwitchWriter", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);  
  17.         public HtmlFilter() { }  
  18.         public HtmlFilter(bool isAvailable)  
  19.         {  
  20.             _isavailable = isAvailable;  
  21.         }  
  22.         public string SetGray(string text)  
  23.         {  
  24.             text = Common.HtmlHelper.Compress(text);  
  25.             if (DateTime.Now.Month == 4 && DateTime.Now.Day == 21)  
  26.             {  
  27.                 text = Regex.Replace(text, "</head>", @"<style type=""text/css"">html {filter:gray;}</style></head>  
  28. ", RegexOptions.IgnoreCase);  
  29.             }  
  30.             return text;  
  31.         }  
  32.         public override void OnActionExecuting(ActionExecutingContext filterContext)  
  33.         {  
  34.             if (!IsAvailable) return;  
  35. #if DEBUG  
  36.             return;  
  37. #endif  
  38.             _originalWriter = (TextWriter)SwitchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { new HtmlTextWriter(new StringWriter()) });  
  39.             base.OnActionExecuting(filterContext);  
  40.         }  
  41.         public override void OnResultExecuted(ResultExecutedContext filterContext)  
  42.         {  
  43.             base.OnResultExecuted(filterContext);  
  44.             if (!IsAvailable) return;  
  45. #if DEBUG  
  46.             return;  
  47. #endif  
  48.   
  49.             HtmlTextWriter cacheWriter = (HtmlTextWriter)SwitchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { _originalWriter });  
  50.             string textWritten = cacheWriter.InnerWriter.ToString();  
  51.             if (filterContext.HttpContext.Response.ContentType == "text/html")  
  52.             {  
  53.                 textWritten = Compress(textWritten).ToString();  
  54.             }  
  55.             filterContext.HttpContext.Response.Write(textWritten);  
  56.             //filterContext.RequestContext.HttpContext.Response.Filter = new HtmlStreamFilter(filterContext.RequestContext.HttpContext.Response.Filter, filterContext.RequestContext.HttpContext.Response.ContentEncoding, SetGray);  
  57.         }  
  58.         private static StringBuilder Compress(string text)  
  59.         {  
  60.             StringBuilder str = new StringBuilder();  
  61.             StringBuilder strlink = new StringBuilder();  
  62.             var s = new char[] { '\f', '\n', '\r', '\t', '\v' };  
  63.             Func<int, object> P = c => null;  
  64.             Func<int, object> Ptag = c => null; //标签处理机  
  65.             Func<int, object> Pcomment = c => null; //注释  
  66.  
  67.             #region - 总处理机 -  
  68.             Func<int, object> state = P = i =>  
  69.             {  
  70.                 char c = text[i];  
  71.                 if (c == '<') //碰到<交个Ptag处理机  
  72.                 {  
  73.                     if (i + 4 < text.Length)  
  74.                     {  
  75.                         if (text.Substring(i + 1, 3) == "!--") //交个注释处理机  
  76.                         {  
  77.                             return Pcomment;  
  78.                         }  
  79.                     }  
  80.                     str.Append(c);  
  81.                     return Ptag;  
  82.                 }  
  83.                 else if (s.Contains(c) == true) { return P; }  
  84.                 else if (c == ' ')  
  85.                 {  
  86.                     if (i + 1 < text.Length)  
  87.                     {  
  88.                         if (s.Union(new char[] { ' ', '<' }).Contains(text[i + 1]) == false)  
  89.                         {  
  90.                             str.Append(c);  
  91.                         }  
  92.                     }  
  93.                     return P;  
  94.                 }  
  95.                 else  
  96.                 {  
  97.                     str.Append(c);  
  98.                     return P;  
  99.                 }  
  100.             };  
  101.             #endregion  
  102.  
  103.             #region - Tag处理机 -  
  104.             Ptag = i =>  
  105.             {  
  106.                 char c = text[i];  
  107.                 if (c == '>') //交还给p  
  108.                 {  
  109.                     str.Append(c);  
  110.                     return P;  
  111.                 }  
  112.                 else if (s.Contains(c) == true) { return Ptag; }  
  113.                 else if (c == ' ')  
  114.                 {  
  115.                     if (i + 1 < text.Length)  
  116.                     {  
  117.                         if (new char[] { ' ', '/', '=', '>' }.Contains(text[i + 1]) == false)  
  118.                         {  
  119.                             str.Append(c);  
  120.                         }  
  121.                     }  
  122.                     return Ptag;  
  123.                 }  
  124.                 else  
  125.                 {  
  126.                     str.Append(c);  
  127.                     return Ptag;  
  128.                 }  
  129.             };  
  130.             #endregion  
  131.  
  132.             #region - 注释处理机 -  
  133.             Pcomment = i =>  
  134.             {  
  135.                 char c = text[i];  
  136.                 if (c == '>' && text.Substring(i - 2, 3) == "-->")  
  137.                 {  
  138.                     return P;  
  139.                 }  
  140.                 else  
  141.                 {  
  142.                     return Pcomment;  
  143.                 }  
  144.             };  
  145.             #endregion  
  146.   
  147.             for (int index = 0; index < text.Length; index++)  
  148.             {  
  149.                 state = (Func<int, object>)state(index);  
  150.             }  
  151.   
  152.             return str;  
  153.         }  
  154.     }  

此方法中新的compress方法使用的GoodSpeed的成果.
posted @ 2014-07-15 09:53  rains  阅读(562)  评论(0编辑  收藏  举报