Система GZip.сеть.пакеты оптимизации


Я использую новую систему.Сеть.Оптимизацию и создали такой пакет:

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")

В fiddler URL встречается с заголовком expires сроком на 1 год в будущем и типом контента text / javascript

В сети.config у меня есть некоторый код для gzip, который работает на статических JS-файлах, но он, кажется, не работает на минифицированных пакетах.

<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%inetpubtempIIS Temporary Compressed Files">
  <scheme name="gzip" dll="%Windir%system32inetsrvgzip.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>

Есть ли способ сделать render bundle gzip содержимым?

3 11

3 ответа:

Как вы заметили, создание пользовательского преобразования пакета путем создания класса, реализующего IBundleTransform, - это правильный путь. Например, ниже приведен пример преобразования bundle, которое использует SharpZipLib (через 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, которое заставило бы разумного человека полагать, что дополнительные свойства ответа могут быть установлены там (как показано выше). Однако это вводит в заблуждение по двум причинам:

  1. Преобразования пакетов выполняются как часть процесса создания пакета - и создание пакета происходит при первой ссылке на него (не разыменовывается, как в, браузер следует за атрибутом src в теге script , но ссылается, как и в представлении, на вызовы сценариев.Вспомогательный метод визуализации). В моем примере выше это означает, что заголовок кодирования содержимого со значением gzip будет установлен на первой странице с представлением, которое использует вспомогательные методы bundling для создания ссылки - и если фактическое содержимое HTTP не gzipped, вы получите ошибку, так как браузер не может декодировать содержимое HTTP.

  2. Даже если #1 не был проблемой, пакет помещен немедленно положите в ASP.NET кэш после его создания-так что этот путь кода будет выполнен только один раз.

Мы внимательно изучаем дизайн в следующей версии framework, чтобы вы могли указать все (в идеале) аспекты ответного сообщения HTTP, которые свободны от контекста HTTP (это означает, что он легко доступен).

Еще одно замечание. Чтобы предоставить пользовательские преобразования пакетов, вам нужно будет вернуться к созданию экземпляра пакета, а не ScriptBundle / StyleBundle. Эти классы на самом деле являются просто сокращенными типами для пакетов с предварительно настроенными преобразованиями пакетов. Чтобы создать пакет на основе пакета, вы должны сделать что-то вроде следующего:

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

С последним классом 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;
    }
}

Затем вы можете использовать GZipStyleBundle и GZipScriptBundle для замены исходных классов Bundle.: StyleBundle, ScriptBundle. Пример:

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

С уважением

Это может быть достигнуто с помощью 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);
        }
    }
}

И зарегистрировать его в config

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