在MVC中从生成的HTML中删除额外的空白

在MVC中从生成的HTML中删除额外的空白,html,asp.net-mvc,compression,whitespace,http-compression,Html,Asp.net Mvc,Compression,Whitespace,Http Compression,我有一个MVC应用程序视图,它正在生成一个相当大的HTML值表(>20MB) 我正在使用压缩过滤器压缩控制器中的视图 internal class CompressFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { HttpRequestBase request = filterCon

我有一个MVC应用程序视图,它正在生成一个相当大的HTML值表(>20MB)

我正在使用压缩过滤器压缩控制器中的视图

 internal class CompressFilter : ActionFilterAttribute
 {
     public override void OnActionExecuting(ActionExecutingContext filterContext)
     {
         HttpRequestBase request = filterContext.HttpContext.Request;
         string acceptEncoding = request.Headers["Accept-Encoding"];
         if (string.IsNullOrEmpty(acceptEncoding))
             return;
         acceptEncoding = acceptEncoding.ToUpperInvariant();
         HttpResponseBase response = filterContext.HttpContext.Response;
         if (acceptEncoding.Contains("GZIP"))
         {
             response.AppendHeader("Content-encoding", "gzip");
             response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
         }
         else if (acceptEncoding.Contains("DEFLATE"))
         {
             response.AppendHeader("Content-encoding", "deflate");
             response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
         }
     }
 }
在运行压缩过滤器之前,是否有办法消除视图中生成的(相当大的)冗余空白(以减少压缩工作负载和大小)

编辑: 我使用下面的Womp建议的空白过滤技术使其工作

以下是Firebug分析的结果:

1) 无压缩,无空白条-21MB,2.59分钟
2) 使用GZIP压缩,无空白带-2MB,17.59s
3) 使用GZIP压缩,空白带-558kB,12.77s


这当然值得。

我想说,如果您的视图生成的数据超过20mb,您可能需要研究不同的方式来显示数据,可能是分页?

如果您从视图返回JSON,它已经缩小,不应该包含任何空格或CR/LF。您应该使用分页来避免一次向浏览器发送如此多的数据。

空白压缩得相当好,我认为删除它不会为您节省太多

如果可能的话,我建议尝试将一些HTML卸载到客户端,使用JavaScript重新构建重复的内容。

编写了一个简洁的小空格压缩程序,它只需通过正则表达式快速运行字节的块拷贝,以去除空间块。他将其作为http模块编写,但您可以从中取出7行主要代码,并将其塞进您的函数中。

\code>#region Stream filter
#region Stream filter
class StringFilterStream : Stream
{
  private Stream _sink;
  private Func<string, string> _filter;

  public StringFilterStream(Stream sink, Func<string, string> filter) {
    _sink = sink;
    _filter = filter;
  }

  #region Mixin Properties/Methods
  public override bool CanRead { get { return true; } }
  public override bool CanSeek { get { return true; } }
  public override bool CanWrite { get { return true; } }
  public override void Flush() { _sink.Flush(); }
  public override long Length { get { return 0; } }
  private long _position;
  public override long Position {
    get { return _position; }
    set { _position = value; }
  }
  public override int Read(byte[] buffer, int offset, int count) {
    return _sink.Read(buffer, offset, count);
  }
  public override long Seek(long offset, SeekOrigin origin) {
    return _sink.Seek(offset, origin);
  }
  public override void SetLength(long value) {
    _sink.SetLength(value);
  }
  public override void Close() {
    _sink.Close();
  }
  #endregion

  public override void Write(byte[] buffer, int offset, int count) {
    // intercept the data and convert to string
    byte[] data = new byte[count];
    Buffer.BlockCopy(buffer, offset, data, 0, count);
    string s = Encoding.Default.GetString(buffer);

    // apply the filter
    s = _filter(s);

    // write the data back to stream
    byte[] outdata = Encoding.Default.GetBytes(s);
    _sink.Write(outdata, 0, outdata.GetLength(0));
  }
}
#endregion

public enum WebWhitespaceFilterContentType
{
  Xml = 0, Css = 1, Javascript = 2
}
public class WebWhitespaceFilterAttribute : ActionFilterAttribute
{
  private WebWhitespaceFilterContentType _contentType;

  public WebWhitespaceFilterAttribute() {
    _contentType = WebWhitespaceFilterContentType.Xml;
  }
  public WebWhitespaceFilterAttribute(WebWhitespaceFilterContentType contentType) {
    _contentType = contentType;
  }

  public override void OnActionExecuting(ActionExecutingContext filterContext) {

    var request = filterContext.HttpContext.Request;
    var response = filterContext.HttpContext.Response;

    switch (_contentType) {
      case WebWhitespaceFilterContentType.Xml:

        response.Filter = new StringFilterStream(response.Filter, s => {
          s = Regex.Replace(s, @"\s+", " ");
          s = Regex.Replace(s, @"\s*\n\s*", "\n");
          s = Regex.Replace(s, @"\s*\>\s*\<\s*", "><");
          // single-line doctype must be preserved
          var firstEndBracketPosition = s.IndexOf(">");
          if (firstEndBracketPosition >= 0) {
            s = s.Remove(firstEndBracketPosition, 1);
            s = s.Insert(firstEndBracketPosition, ">\n");
          }
          return s;
        });
        break;

      case WebWhitespaceFilterContentType.Css:
      case WebWhitespaceFilterContentType.Javascript:

        response.Filter = new StringFilterStream(response.Filter, s => {
          s = Regex.Replace(s, @"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/", "");
          s = Regex.Replace(s, @"\s+", " ");
          s = Regex.Replace(s, @"\s*{\s*", "{");
          s = Regex.Replace(s, @"\s*}\s*", "}");
          s = Regex.Replace(s, @"\s*;\s*", ";");
          return s;
        });
        break;
    }
  }
}
类StringFilterStream:流 { 私人溪流(水槽);; 私有函数过滤器; 公共StringFilterStream(流接收器、Func过滤器){ _水槽=水槽; _过滤器=过滤器; } #区域混合属性/方法 公共重写bool CanRead{get{return true;}} 公共覆盖布尔CanSeek{get{return true;}} 公共覆盖布尔可写{get{return true;}} 公共重写void Flush(){u sink.Flush();} 公共重写长长度{get{return 0;}} 私人多头头寸; 公众优先多头仓位{ 获取{return\u position;} 设置{u位置=值;} } 公共重写整型读取(字节[]缓冲区、整型偏移量、整型计数){ 返回_sink.Read(缓冲区、偏移量、计数); } 公共覆盖长寻道(长偏移,参见原始坐标系){ 返回_sink.Seek(偏移,原点); } 公共覆盖无效设置长度(长值){ _sink.SetLength(值); } 公共覆盖无效关闭(){ _sink.Close(); } #端区 公共重写无效写入(字节[]缓冲区、整数偏移量、整数计数){ //截取数据并转换为字符串 字节[]数据=新字节[计数]; 块复制(缓冲区、偏移量、数据、0、计数); 字符串s=Encoding.Default.GetString(缓冲区); //应用过滤器 s=_过滤器; //将数据写回流 byte[]outdata=Encoding.Default.GetBytes; _sink.Write(outdata,0,outdata.GetLength(0)); } } #端区 公共枚举WebWhitespaceFilterContentType { Xml=0,Css=1,Javascript=2 } 公共类WebWhitespaceFilterAttribute:ActionFilterAttribute { 私有WebWhitespaceFilterContentType\u contentType; 公共WebWhitespaceFilterAttribute(){ _contentType=WebWhitespaceFilterContentType.Xml; } 公共WebWhitespaceFilterAttribute(WebWhitespaceFilterContentType内容类型){ _contentType=contentType; } 公共覆盖无效OnActionExecuting(ActionExecutingContext filterContext){ var request=filterContext.HttpContext.request; var response=filterContext.HttpContext.response; 开关(_contentType){ 案例WebWhitespaceFilterContentType.Xml: response.Filter=新的StringFilterStream(response.Filter,s=>{ s=Regex.Replace(s,@“\s+”,“”); s=Regex.Replace(s,@“\s*\n\s*”,“\n”); s=Regex.Replace(s,@“\s*\>\s*\”; 如果(firstEndBracketPosition>=0){ s=s.移除(第一端支架位置,1); s=s.Insert(firstEndBracketPosition,“>\n”); } 返回s; }); 打破 case WebWhitespaceFilterContentType.Css: case WebWhitespaceFilterContentType.Javascript: response.Filter=新的StringFilterStream(response.Filter,s=>{ s=Regex.Replace(s,@/\*([^*].[\r\n].[\r\n]。(\*+([^*/].[\r\n]))*\*+/“,”; s=Regex.Replace(s,@“\s+”,“”); s=Regex.Replace(s,@“\s*{\s*”,“{”); s=Regex.Replace(s,@“\s*}\s*”,“}”); s=Regex.Replace(s,@“\s*;\s*”,“;”; 返回s; }); 打破 } } }
以下是我在项目中使用的空白过滤器属性的VB.NET版本:

#地区“进口”
导入System.IO
#末端区域
命名空间MyCompany.Web.Mvc.Extensions.ActionFilters
''' 
''WhitespaceFilter属性
''' 
公共不可继承类WhitespaceFilterAttribute
继承ActionFilterAttribute
''' 
在执行操作时调用“”。
''' 
''过滤器上下文。
''' 
公共覆盖子OnActionExecuting(filterContext作为ActionExecutingContext)
filterContext.HttpContext.Response.Filter=新的WhitespaceFilterStream(filterContext.HttpContext.Response.Filter)
端接头
#区域“空白流过滤器”
''' 
''空白流过滤器
''' 
私有类WhitespaceFilterStream
继承流
#区域“宣言”
“瓦尔斯议员。

私有共享regexpatern作为新的Regex((?@womp)已经提出了一种很好的方法,但是该模块已经过时了。我一直在使用它,但它不是一种最佳的方法。下面是我提出的问题:

这是
public class RemoveWhitespacesAttribute : ActionFilterAttribute {

    public override void OnActionExecuted(ActionExecutedContext filterContext) {

        var response = filterContext.HttpContext.Response;

        //Temp fix. I am not sure what causes this but ContentType is coming as text/html
        if (filterContext.HttpContext.Request.RawUrl != "/sitemap.xml") {

            if (response.ContentType == "text/html" && response.Filter != null) {
                response.Filter = new HelperClass(response.Filter);
            }
        }
    }

    private class HelperClass : Stream {

        private System.IO.Stream Base;

        public HelperClass(System.IO.Stream ResponseStream) {

            if (ResponseStream == null)
                throw new ArgumentNullException("ResponseStream");
            this.Base = ResponseStream;
        }

        StringBuilder s = new StringBuilder();

        public override void Write(byte[] buffer, int offset, int count) {

            string HTML = Encoding.UTF8.GetString(buffer, offset, count);

            //Thanks to Qtax
            //https://stackoverflow.com/questions/8762993/remove-white-space-from-entire-html-but-inside-pre-with-regular-expressions
            Regex reg = new Regex(@"(?<=\s)\s+(?![^<>]*</pre>)");
            HTML = reg.Replace(HTML, string.Empty);

            buffer = System.Text.Encoding.UTF8.GetBytes(HTML);
            this.Base.Write(buffer, 0, buffer.Length);
        }

        #region Other Members

        public override int Read(byte[] buffer, int offset, int count) {

            throw new NotSupportedException();
        }

        public override bool CanRead{ get { return false; } }

        public override bool CanSeek{ get { return false; } }

        public override bool CanWrite{ get { return true; } }

        public override long Length{ get { throw new NotSupportedException(); } }

        public override long Position {

            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        public override void Flush() {

            Base.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin) {

            throw new NotSupportedException();
        }

        public override void SetLength(long value) {

            throw new NotSupportedException();
        }

        #endregion
    }

}