【问题标题】:GZip system.web.optimization bundlesGZip system.web.optimization 包
【发布时间】:2012-07-03 06:58:07
【问题描述】:

我正在使用新的 System.Web.Optimization 并创建了一个这样的包:

bundles.Add(New ScriptBundle("~/bundles/BaseJS").Include(
                "~/Resources/Core/Javascripts/jquery-1.7.1.js",
                "~/Resources/Core/Javascripts/jquery-ui-1.8.16.js",
                "~/Resources/Core/Javascripts/jquery.validate.js",
                "~/Resources/Core/Javascripts/jquery.validate.unobtrusive.js",
                "~/Resources/Core/Javascripts/jquery.unobtrusive-ajax.js"))

在我看来我已经添加了这个

@System.Web.Optimization.Scripts.Render("~/bundles/BaseJS")

在提琴手中,该 URL 带有一个未来 1 年的过期标头和一个 text/javascript 的内容类型

在 web.config 中,我有一些用于处理静态 JS 文件的 gzip 代码,但它似乎不适用于缩小的捆绑包。

<staticContent>
  <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00"/>
  <remove fileExtension=".js"/>
  <mimeMap fileExtension=".js" mimeType="text/javascript"/>
</staticContent>
<urlCompression doDynamicCompression="true" doStaticCompression="true" dynamicCompressionBeforeCache="true"/>
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
  <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll"/>
  <dynamicTypes>
    <add mimeType="text/*" enabled="true"/>
    <add mimeType="text/javascript" enabled="true"/>
  </dynamicTypes>
  <staticTypes>
    <add mimeType="text/*" enabled="true"/>
    <add mimeType="text/javascript" enabled="true"/>
  </staticTypes>
</httpCompression>

有没有办法让渲染包 gzip 内容?

【问题讨论】:

标签: asp.net-mvc-3 asp.net-optimization bundling-and-minification


【解决方案1】:

使用最新的ASP.NET Optimization (v1.1.2)GZipTransform 类不能正常工作。

我发现了一种使用自定义 Bundle 类的新方法,该类将始终在响应之前压缩捆绑内容(已转换和缓存):

public class GZipBundle : Bundle
{
    public GZipBundle(string virtualPath, params IBundleTransform[] transforms)
        : base(virtualPath, null, transforms) { }

    public override BundleResponse CacheLookup(BundleContext context)
    {
        if (null != context) GZipEncodePage(context.HttpContext);
        return base.CacheLookup(context);
    }

    // Sets up the current page or handler to use GZip through a Response.Filter.
    public static void GZipEncodePage(HttpContextBase httpContext)
    {
        if (null != httpContext && null != httpContext.Request && null != httpContext.Response
            && (null == httpContext.Response.Filter
            || !(httpContext.Response.Filter is GZipStream || httpContext.Response.Filter is DeflateStream)))
        {
            // Is GZip supported?
            string acceptEncoding = httpContext.Request.Headers["Accept-Encoding"];
            if (null != acceptEncoding
                && acceptEncoding.IndexOf(DecompressionMethods.GZip.ToString(), StringComparison.OrdinalIgnoreCase) >= 0)
            {
                httpContext.Response.Filter = new GZipStream(httpContext.Response.Filter, CompressionMode.Compress);
                httpContext.Response.AddHeader("Content-Encoding", DecompressionMethods.GZip.ToString().ToLowerInvariant());
            }
            else if (null != acceptEncoding
                && acceptEncoding.IndexOf(DecompressionMethods.Deflate.ToString(), StringComparison.OrdinalIgnoreCase) >= 0)
            {
                httpContext.Response.Filter = new DeflateStream(httpContext.Response.Filter, CompressionMode.Compress);
                httpContext.Response.AddHeader("Content-Encoding", DecompressionMethods.Deflate.ToString().ToLowerInvariant());
            }

            // Allow proxy servers to cache encoded and unencoded versions separately
            httpContext.Response.AppendHeader("Vary", "Content-Encoding");
        }
    }
}

// Represents a bundle that does CSS minification and GZip compression.
public sealed class GZipStyleBundle : GZipBundle
{
    public GZipStyleBundle(string virtualPath, params IBundleTransform[] transforms) : base(virtualPath, transforms) { }
}

// Represents a bundle that does JS minification and GZip compression.
public sealed class GZipScriptBundle : GZipBundle
{
    public GZipScriptBundle(string virtualPath, params IBundleTransform[] transforms)
        : base(virtualPath, transforms)
    {
        base.ConcatenationToken = ";" + Environment.NewLine;
    }
}

然后你可以使用GZipStyleBundleGZipScriptBundle替换原来的Bundle类:StyleBundleScriptBundle。例如:

public static class BundleConfig
{
    // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new GZipScriptBundle("~/bundles/jquery.js").Include(...));
        bundles.Add(new GZipScriptBundle("~/bundles/jquery-ui.js", new JsMinify()).Include(...));

        bundles.Add(new GZipStyleBundle("~/bundles/all.css", new CssMinify()).Include(...));
    }
}

问候

【讨论】:

    【解决方案2】:

    如您所述,通过创建实现 IBundleTransform 的类来创建自定义捆绑转换是正确的方法。例如,以下是使用 SharpZipLib (via NuGet) 进行 GZip 压缩的示例包转换:

    public class GZipTransform : IBundleTransform 
    {
        string _contentType;
    
        public GZipTransform(string contentType)
        {
            _contentType = contentType;
        }
    
        public void Process(BundleContext context, BundleResponse response)
        {
            var contentBytes = new UTF8Encoding().GetBytes(response.Content);
    
            var outputStream = new MemoryStream();
            var gzipOutputStream = new GZipOutputStream(outputStream);
            gzipOutputStream.Write(contentBytes, 0, contentBytes.Length);
    
            var outputBytes = outputStream.GetBuffer();
            response.Content = Convert.ToBase64String(outputBytes);
    
    
            // NOTE: this part is broken
            context.HttpContext.Response.Headers["Content-Encoding"] = "gzip";
            response.ContentType = _contentType ;
        }
    }
    

    现在,不幸的是,在测试这个样本时,我发现了一个会导致它无法工作的错误。最初的设计期望人们会做一些非常简单的事情——因此,BundleResponse 公开了允许您设置内容(更具体地说,字符串内容)和内容类型的属性。 BundleContext 暴露了 HttpContext 的一个属性,这会使一个有理智的人相信可以在那里设置响应的附加属性(如上所示)。但是,这会产生误导,原因有两个:

      1234563如中,视图调用 Scripts.Render 辅助方法)。在我上面的示例中,这意味着将在第一页上设置一个值为 gzip 的内容编码标头,其视图使用捆绑的辅助方法来生成链接 - 如果实际的 HTTP 内容没有经过 gzip 压缩,你由于浏览器无法解码 HTTP 内容,因此会出现错误。
    1. 即使 #1 不是问题,捆绑包在创建后会立即放入 ASP.NET 缓存中 - 因此此代码路径只会执行一次。

    我们正在仔细研究下一版本框架的设计,以使您能够指定不受 HTTP 上下文影响的 HTTP 响应消息的所有(理想情况下)方面(这意味着它很容易缓存)。

    一个附加说明。要提供自定义捆绑转换,您需要回退到创建 Bundle 的实例而不是 ScriptBundle/StyleBundle。这些类实际上只是具有预配置捆绑转换的捆绑的简写类型。要基于 Bundle 创建一个捆绑包,您需要执行以下操作:

    var jqueryBundle = new Bundle("~/bundles/jqueryall", new GZipTransform("text/javascript"));
    jqueryBundle.Include("~/Scripts/jquery-1.*",
        "~/Scripts/jquery-ui*",
        "~/Scripts/jquery.unobtrusive*",
        "~/Scripts/jquery.validate*");
    bundles.Add(jqueryBundle);
    

    【讨论】:

    • 事实证明,IIS 动态内容压缩无需任何仪式即可完成此操作。在我的情况下,我必须在安装 DCC 后进行 IISReset 才能使其工作,所以我被误导认为 system.web.optimization 需要对 gzip 进行干预,但事实并非如此。
    • 您写道“我们正在认真研究下一个版本的框架的设计...”,所以我想您是 ASP.NET 团队的成员。您是否计划在下一个版本中允许字节内容?这对于图像捆绑(精灵)非常有用。
    • 这个问题在 System.Web.Optimizations 中修复过吗?我处于无法使用 IIS 压缩的环境中,因此这似乎是除了使用 HttpModule 之外唯一可行的方法。
    • 我在本地运行 IIS,但 IISReset 对我不起作用。在 DCC 生效之前,我必须重新启动计算机。
    【解决方案3】:

    可以使用HttpModule来实现

    public class GzipModule : IHttpModule
    {
        #region IHttpModule Members
    
        public void Init(HttpApplication application)
        {
            application.BeginRequest += Application_BeginRequest;
        }
    
        public void Dispose()
        {
        }
    
        #endregion
    
        private void Application_BeginRequest(Object source, EventArgs e)
        {
            HttpContext context = HttpContext.Current;
            HttpRequest request = context.Request;
            HttpResponse response = context.Response;
            string acceptEncoding = request.Headers["Accept-Encoding"];
    
            if (String.IsNullOrEmpty(acceptEncoding))
                return;
    
            acceptEncoding = acceptEncoding.ToUpperInvariant();
    
            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);
            }
        }
    }
    

    并在配置中注册

      <system.webServer>
        <modules>
            <add name="Gzip" type="Gecko.Web.GzipModule" />
        </modules>
    

    【讨论】:

    • 是的,但是现在它将针对所有请求运行,而不仅仅是捆绑包。
    猜你喜欢
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    • 2013-09-24
    • 2014-09-25
    • 1970-01-01
    • 2014-09-25
    • 1970-01-01
    相关资源
    最近更新 更多