在MVC中从生成的HTML中删除额外的空白
我有一个MVC应用程序视图,它正在生成一个相当大的HTML值表(>20MB) 我正在使用压缩过滤器压缩控制器中的视图在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
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
}
}