ActionFilterAttribute之HtmlFilter,压缩HTML代码

当开启这个过滤器后,最终生成的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的成果.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值