11

Я использую новый System.Web.Optimization и создали пакет, как это:GZip 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") 

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

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

<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 содержимым?

+0

Я думаю, что это может быть возможно с iBundleTransform, но я не знаю, как. https://twitter.com/ericdc1/status/219850852364599298 – ericdc

ответ

12

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

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

  2. Даже если # 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); 
+4

Оказывается, что сжатие IIS Dynamic Content Compression делает это без всякой церемонии. В моем случае я должен был выполнить IISReset после установки DCC, чтобы он работал, поэтому я был введен в заблуждение, думая, что system.web.optimization требует вмешательства в gzip, но это не так. – ericdc

+0

Вы пишете «Мы внимательно изучаем дизайн в следующей версии фреймворка ...», поэтому, я полагаю, вы являетесь членом команды ASP.NET. Планируете ли вы разрешить содержимое байта в следующей версии? Это было бы чрезвычайно полезно для связывания изображений (спрайтов). – JohannesH

+2

Было ли это зафиксировано в System.Web.Optimizations? Я в среде, где я не могу использовать сжатие IIS, поэтому это кажется единственным жизнеспособным способом, кроме использования HttpModule. – kkara

5

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

Правда, но теперь это будет выполняться для ВСЕХ запросов, а не просто для пакетов. –

11

С последние 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(...)); 
    } 
} 

С уважением