Сжатие web-содержимого посредством gzip (GNU zip) — это довольно старая технология. Суть ее сводится к тому, что web-содержимое перед отправкой пользователю сжимается по известному всем алгоритму zip. Сама спецификация gzip описана в RFC1952, версия 4.2 которой датируется маем 1996 года. На сегодняшний день все популярные браузеры и веб-серверы поддерживают сжатие web-содержимого посредством gzip. В этой статье я постараюсь рассказать о нескольких способах включения в проекты asp.net поддержки gzip-сжатия.
Первый способ самый простой. Вы просто нажимаете пару кнопок в настройках IIS, и вот уже web-сервер обеспечивает вам автоматическое сжатие для клиентов, которые gzip поддерживают.
Для тех, кто не знает, как в IIS включить сжатие, рассказываю: необходимо в диспетчере служб IIS зайти в свойства элемента «Веб узлы» и перейти во вкладку «Служба».
Данный способ самый простой, но вместе с тем не самый гибкий.
Плюсы такого включения сжатия:
Минусы включения поддержки сжатия на сервере IIS:
Суть этого метода, описанного в Интернете во многих источниках, например, тут http://www.stardeveloper.com/articles/di..., состоит в том, что вы, используя, появившиеся в .net framework 2.0 классы System.IO.Compression.GZipStream и System.IO.Compression.DeflateStream определяете свой собственный фильтр содержимого http-запроса, который перед отправкой клиенту, сжимает данные посредством методов GZipStream. Особенность этого метода в том, что все действия по сжатию содержимого производятся в global.asax в методе Application_BeginRequest, который вызывается перед запросом пользователя. Тем самым можно создать фильтр на любой запрос пользователя для отправки всего содержимого Response в сжатом виде. Вот как этот метод выглядит в исходном виде (взято с сайта указанного выше):
<%@ Application Language="C#" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="System.IO.Compression" %> <script runat="server"> void Application_BeginRequest(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; string acceptEncoding = app.Request.Headers["Accept-Encoding"]; Stream prevUncompressedStream = app.Response.Filter; if (acceptEncoding == null || acceptEncoding.Length == 0) return; acceptEncoding = acceptEncoding.ToLower(); if (acceptEncoding.Contains("gzip")) { // gzip app.Response.Filter = new GZipStream(prevUncompressedStream, CompressionMode.Compress); app.Response.AppendHeader("Content-Encoding", "gzip"); } else if (acceptEncoding.Contains("deflate")) { // deflate app.Response.Filter = new DeflateStream(prevUncompressedStream, CompressionMode.Compress); app.Response.AppendHeader("Content-Encoding", "deflate"); } } </script>
Основные плюсы данного метода:
Минусы данного метода:
Надо заметить, что проблема с ajax.net возможно имеет решение, но мне на момент написания статьи такое решение не известно. Я буду благодарен, если кто-нибудь опишет реализацию данного способа включения gzip в ajax.net проектах.
Отличный специалист Rick Strahl на своем блоге, http://west-wind.com/WebLog/default.aspx, приводит еще один, альтернативный способ сжатия web-страниц посредство gzip. Рассмотрим пару функций которые он написал, а я сделал статическими:
public static bool IsGZipSupported() { string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"]; if (!string.IsNullOrEmpty(AcceptEncoding) && (AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate"))) return true; return false; } public static void GZipEncodePage() { if (IsGZipSupported()) { HttpResponse Response = HttpContext.Current.Response; string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"]; if (AcceptEncoding.Contains("gzip")) { Response.Filter = new System.IO.Compression.GZipStream(Response.Filter, System.IO.Compression.CompressionMode.Compress); Response.AppendHeader("Content-Encoding", "gzip"); } else { Response.Filter = new System.IO.Compression.DeflateStream(Response.Filter, System.IO.Compression.CompressionMode.Compress); Response.AppendHeader("Content-Encoding", "deflate"); } } }
Первый метод просто проверяет поддержку клиентом технологии сжатия, а вот второй очевидно практически равносилен методу из примера о Application_BeginRequest, только в данном случае вынесен в отдельную функцию. Что это дает? Ну, во-первых, рассмотрим использование:
protected void Page_Load(object sender, EventArgs e) { HtmlUtil.GZipEncodePage(); }
Очевидно, что данный метод работает на уровне пользовательских страниц. Работает только с телом страницы и не обрабатывает js,css и любые другие файлы.
Плюсы этого подхода:
Минусы:
Очень важно, при использовании этого метода вызывать его до любой первой записи в Response.
Лично я остановился на третьем варианте. Во-первых, часть моего проекта написана на ajax.net и второй вариант у меня не заработал. Во-вторых, я столкнулся с проблемой при включении централизованного сжатия на уровне IIS, следовательно, и первый вариант мне не подошел. В-третьих, третий подход лично мне кажется элегантным и гибким. Для сжатия js и css можно поискать и другие способы, а сжатие одних только aspx уже дает заметный выигрыш в размере трафика.
Приведу некоторые данные:
Для себя делаю вывод, что сжатие одних только страниц позволяет значительно уменьшить размер передаваемых данных, а для ajax-ориентированного проекта бонусом пойдет еще и уменьшение размера всех ajax-данных передаваемых клиенту.