Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing extra whitespace from generated HTML in MVC

I have an MVC application view that is generating quite a large HTML table of values (>20MB).

I am compressing the view in the controller using a compression filter

 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);
         }
     }
 }

Is there a way to also eliminate the (quite large) amount of redundant whitespace generated in the view before I run the compress filter (to reduce compression workload and size)?

EDIT: I got it working using the WhiteSpaceFilter technique suggested by Womp below.

For interest here's the results, as analysed by Firebug:

1) No Compression, no whitespace strip - 21MB, 2.59 minutes
2) With GZIP compression, no whitespace strip - 2MB, 17.59s
3) With GZIP compression, whitespace strip - 558kB, 12.77s

So certainly worth it.

like image 310
WOPR Avatar asked May 13 '09 00:05

WOPR


2 Answers

This guy wrote a neat little whitespace compactor that simply runs a fast block copy of your bytes through a regular expression to strip out blobs of space. He wrote it as an http module, but you could take the 7 lines of workhorse code out of it and plop it into your function.

like image 142
womp Avatar answered Nov 07 '22 07:11

womp


@womp has already suggested a good way of doing it but that module is pretty outdated. I have been using that but it turns out that it is not an optimal way. Here is the question I asked about:

Remove white space from entire Html but inside pre with regular expressions

Here is how I do it:

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
    }

}
like image 7
tugberk Avatar answered Nov 07 '22 06:11

tugberk