Браузер в каждом запросе к серверу в поле "Accept-Encoding" может указать, какие методы сжатия он поддерживает. Сервер, отвечая на запрос, может выбрать один из указанных браузером методов и, высылая сжатое тело ответа, указать в заголовке (в поле "Content-Encoding"), какой именно метод был выбран.
Вот, к примеру, поле "Accept-Encoding" браузера Opera 10.00:
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Браузер указал, что поддерживаются два метода сжатия:
На данный момент браузеры совокупно поддерживают следующие методы сжатия:
Метод sdch сильно отличается от прочих и имеет смысл только для группы страницы, эффективность же остальных методов проще оценить на примере HTML-страницы, полученной склейкой первых страниц нескольких новостных изданий:
| Без сжатия | gzip/ |
bzip2 | Compress |
|---|---|---|---|
| 587828 | 132023 | 88263 | 222091 |
У
Худший результат типично показывает метод compress, по этой причине многие браузеры отказались от поддержки этого метода. Лучший результат — у bzip2, но этот метод, весьма требовательный к ресурсам, лишь недавно стал появляться в браузерах — на данный момент его поддерживают OmniWeb, w3m, lynx и ранние версии Google Chrome.
| Эффективность | Gzip | bzip2 |
|---|---|---|
| Исходный размер | 189058 Б | 189058 Б |
| 1 | 3,333 мс / 44572 Б | 40,282 мс / 32247 Б |
| 2 | 3,519 мс / 42685 Б | 43,716 мс / 29690 Б |
| 3 | 4,713 мс / 42685 Б | 43,765 мс / 29690 Б |
| 4 | 5,274 мс / 39111 Б | 44,213 мс / 29690 Б |
| 5 | 6,526 мс / 37039 Б | 43,704 мс / 29690 Б |
| 6 | 8,036 мс / 36206 Б | 43,814 мс / 29690 Б |
| 7 | 9,098 мс / 35940 Б | 43,934 мс / 29690 Б |
| 8 | 12,87 мс / 35713 Б | 43,725 мс / 29690 Б |
| 9 | 14,319 мс / 35707 Б | 45,129 мс / 29690 Б |
На сайте языка программирования PHP, в комментариях к описанию
функции gzcompress есть результаты, иллюстрирующие, насколько
может зависеть затраченное время и эффективность сжатия
Кажущееся странным поведение
Что в точности означает этот параметр, для нас маловажно, существенно то, что если выбранный файл (исходный размер — 184,5 Кб) целиком помещается в блок, алгоритм bzip2 не улучшает результат при увеличении размера блока, но сжатие занимает больше времени.
Вывод — если вы настраиваете на сервере сжатие bzip2 в реальном времени, выбирайте размер блока чуть большим, чем средний размер страниц вашего сайта.
У gzip, как видно, зависимость однозначная, оптимальная стратегия в данном случае — установить такой параметр эффективности сжатия, который ваш сервер выдержит, с некоторым запасом.
Существуют также аппаратные решения, позволяющие эффективно сжимать трафик алгоритмом gzip со скоростью вплоть до 10 гигабит в секунду, например, платы расширения фирмы Comtech AHA Corporation.
К сожалению, несмотря на давность, реализация сжатия в браузерах не обходится без проблем. Особенно печально, что проблемы есть даже в браузерах, выпущенных недавно относительно момента написания книги.
Некоторые браузеры, не испытывая проблем с распаковкой HTML, тем не менее неправильно обрабатывают сжатые файлы CSS и JavaScript.
Поддерживает методы gzip и
На данный момент у этого старейшего из используемых браузеров неизвестны какие-либо проблемы, связанные со сжатием.
Поддерживает методы gzip и
К сожалению, у этого браузера взаимоотношения со сжатым контентом складываются не так радужно, как хотелось бы. Хотя современные версии браузеров, по всей видимости, не имеют ошибок, связанных со сжатием, но все еще распространенные предыдущие версии содержат значительное их количество.
Проблемными являются версии, начиная с четвертой, заканчивая Internet Explorer 6.0 SP1. Хорошая новость, что в Service Pack 2 этого браузера все проблемы исправлены. К счастью, эта версия отличается полем "Useragent ": его значение содержит подстроку "SV1" ("Security Version 1"), что позволяет отличить эту версию еще на сервере.
Некоторые из ошибок этого браузера можно обойти, но так как распространенность его проблемных версий составляет доли процента, то игра, вероятно, не стоит свеч.
Доля четвертой и пятой версий в совокупности не превышает 0,5% и продолжает снижаться, с шестой версией несколько сложнее — исследований, определяющих долю IE6SP1, нет, но можно получить оценку сверху: 13%*, если вычесть все IE6, установленные на Windows Vista, где в браузере уже содержится SP2.
Поддерживает gzip и
Данный браузер не слишком широко известен, но его версии до 3.1
содержат ошибки в реализации метода
Также есть сообщения о проблемах со сжатыми методом gzip файлами JavaScript и CSS. Впрочем, доля этого браузера исчезающе мала (менее 0,1%).
Поддерживает gzip и
Firefox версий до 3.0x включительно в редких случаях загружает сжатые файлы CSS и JavaScript, не распаковывая их, кроме того, версия 3.0 не правильно загружает файлы этих типов по протоколу HTTPS, если используется Keep-Alive.
Доля проблемных браузеров этого типа — около 26%.
Версии до четвертой включительно неправильно обрабатывают сжатые JavaScript- и CSS-файлы, кроме того, если URL, по которому они скачиваются, заканчивается на .gz, они распознаются как архивы.
Поддерживает gzip,
Самый молодой из современных браузеров во второй версии испытывает проблемы с распаковкой CSS и JavaScript: в некоторых подверсиях браузер не пытается распаковать эти файлы, в других загружает их не до конца.
Доля проблемной версии в зарубежном и российском Интернете не превышает 6%.
Метод bzip2 было решено отключить в новых версиях браузера из-за проблем с некоторыми прокси-серверами, которые искажают заголовок ответа, если тело сжато этим методом.
Некоторые типы прокси и firewall имеют ряд проблем, затрудняющих использование сжатия контента.
Первая проблема связана с кэшированием страниц на стороне прокси-сервера. Поскольку через один прокси могут работать несколько клиентов с разными браузерами, одна и та же страница может загружаться, в зависимости от браузера, в сжатом или несжатом виде. Прокси необходимо различать эти две ситуации и отдавать разные страницы в зависимости от возможностей клиента.
Для этого используется заголовок
Некоторые довольно распространенные прокси (например, WinGate, Kerio WinRoute) неправильно обрабатывают эту ситуацию, поэтому в том случае, если запрос идет через прокси, контент либо не сжимают вовсе, либо высталяют заголовки, препятствующие кэшированию его на прокси (например, при помощи заголовка Cache-control: private).
Отличить проксированный запрос от непроксированного можно по наличию заголовка Via, которые прокси обязаны выставлять в HTTP-запросах версии 1.1 (RFC 2616). Для запросов HTTP 1.0 такого флага нет, и обычно любой запрос этой версии на всякий случай считается проксируемым.
Вторая проблема подробно обсуждалась в докладе Тони Джентил-кора (он работает в GoogLe) на
Некоторые firewaLL и прокси просто искажают заголовок (например, вместо Accept-Encoding вы получите X-
Один из способов решить эту проблему, который сразу приходит в голову, — основываться не на Accept-Encoding, а на User-agent, в конце концов мы же знаем, что Internet ExpLorer 8.0 или Firefox 3.5 уж точно поддерживают gzip. Очевидный недостаток — необходимо наличие достаточно большого словаря браузеров, который к тому же нужно пополнять.
В докладе предлагается другой способ: при отсутствии заголовка Accept-Encoding загружать в скрытом фрейме (<iframe>) сжатый некэ-шируемый HTML с JavaScript внутри, который выставляет cookie через JavaScript. Если браузер способен распаковать этот документ, то cookie будет выставлена. В дальнейшем нужно ориентироваться, помимо заголовков, еще и на этот флаг.
Эта несложная идея хороша тем, что полностью автоматизирует процесс и не содержит риска ложного срабатывания.
Существуют два разных подхода к использованию сжатия на стороне сервера: сжимать данные "на лету" и использовать предварительно сжатые файлы. Первый выгоднее с точки зрения актуализации: не нужно заботиться, не устарел ли тот или иной файл; второй метод экономнее обходится с ресурсами машины, но приходится держать две копии файла для тех браузеров, которые не поддерживают сжатие или поддерживают его с ошибками.
К счастью, методы можно успешно комбинировать, что позволяет сочетать достоинства того и другого.
Для Apache 1.x существуют два модуля, осуществляющих сжатие контента: это mod_deflate и mod_gzip. Оба модуля разработаны сторонними разработчиками. Первый представляет собой
В Apache 2.x "из коробки" входит модуль mod_deflate (которые не имеет ничего общего с модулем для Apache 1.x).
Если есть возможность, настоятельно рекомендуем использовать для
Apache 1.x модуль mod_deflate, он позволяет более тонко обходить
ошибки браузера, имеет настройки степени сжатия плюс обладает лучшей
производительностью. Ниже приведен оптимальный, на наш взгляд,
конфигурационный файл этого модуля.
Для эффективного управления модулем необходим также модуль mod_setenvif, он позволит отключать сжатие для проблемных браузеров.
# Apache 1.x mod_deflate (необходим mod_setenvif) # включаем модуль DeflateEnable on # включаем обход ошибки Internet Explorer 4.0, связанной с докачкой # сжатого контента DeflateDisableRange "MSIE 4." # на данный момент все прокси корректно обрабатывают сжатый # ответ, поэтому мы включаем сжатие, даже если использован # прокси DeflateHTTP 1.0 # сжиматься будут только ответы, не кэшируемые прокси-серверами, это # позволяет избежать проблем с WinRoute, Kerio WinRoute и другими DeflateProxied poor_cachable # эта директива включает заголовок Vary для всех ответов, # IE версий 4.0—6.0SP1 не кэшируют ответы, если сжатие выключено, # но получен заголовок Vary, но эти версии сейчас используются # очень мало DeflateVary on # уровень сжатия — максимальный DeflateCompLevel 9 DeflateHash 128 DeflateWindow 32 # включаем игнорирование вызова ap_bflush, это улучшает сжатие # ответов некоторых приложений (например, написанных # с применением Chili!Soft ASP). Если ваше приложение намеренно # вызывает этот метод, опцию лучше отключить DeflateIgnoreFlush on # минимальный размер, при котором ответ сервера будет сжиматься DeflateMinLength 100 # настраиваем, что сжимать DeflateType text/html text/css text/xml application/xml image/x-icon DeflateType application/x-javascript text/plain # исключаем проблемные случаи по умолчанию выключаем сжатие # на CSS и JavaScript (оно будет разрешено # ниже для браузеров, не имеющих проблем, специальный флаг "no_gzip" # говорит модулю, что сжатие надо выключить) SetEnvIf Content-Type text/css no_gzip SetEnvIf Content-Type application/x-javascript no_gzip SetEnvIf Content-Type image/x-icon no_gzip # выключаем сжатие для MSIE < 6SP1, # исключаем притворяющуюся IE "Оперу" BrowserMatch "MSIE [456]" no_gzip BrowserMatch "SV1;" !no_gzip BrowserMatch "Opera" !no_gzip BrowserMatch "MSIE ([789]| [1-9][0-9])" !no_gzip # не имеет проблем Firefox > 3 BrowserMatch "Firefox/(3\.[^0]|[4-9]|[1-9][0-9])" !no_gzip
Образец конфигурационного файла модуля mod_gzip взят из книги
"Разгони свой сайт", в данной редакции комментарии были несколько
расширены, а в настройку внесены правки.
Уровень эффективности сжатия не регулируется настройками модуля и
может быть изменен только правкой исходного кода (строка gz1-level = 6
в функции gz1_init ).
Упомянутый недостаток mod_gzip — недостаточно гибкие опции кон-
фигурирования не дают выборочно включить сжатие CSS и JS только в тех
браузерах, где это не создает проблем. По этой причине сжатие таких
файлов отключено в данной конфигурации.
# Apache 1.x mod_gzip
<IfModule mod_gzip.c="">
# включаем gzip
mod_gzip_on Yes
# если рядом с запрашиваемым файлом есть сжатая версия
# с расширением .gz, то будет отдана именно она, ресурсы CPU
# расходоваться не будут
mod_gzip_can_negotiate Yes
# используем при статическом архивировании расширение .gz
mod_gzip_static_suffix .gz
# выставляем для статически архивированных файлов
# заголовок Content-Encoding: gzip
AddEncoding gzip .gz
# не обновлять самостоятельно статически архивированные файлы,
# при включении этой опции вам не нужно самостоятельно проверять
# актуальность сжатых данных, но нужно позаботиться о том,
# что процесс веб-сервера имел доступ на запись к сжатым файлам
mod_gzip_update_static No
# выставляем минимальный размер для сжимаемого файла, файлы
# меньшего размера сжимать неэффективно:
mod_gzip_minimum_file_size 1000
# и максимальный размер файла, слишком большие файлы будут
# сжиматься очень долго, поскольку mod_gzip не умеет сжимать
# данные по мере их получения, то пользователь начнет получать
# файл с задержкой, только после того как он будет полность сжат
mod_gzip_maximum_file_size 500000
# выставляем максимальный размер файла, сжимаемого прямо
# в памяти, чем больше эта величина, тем больше производительность
# и тем больше расход памяти каждый процессом, 60000 — максимальное
# значение для этой величины, поскольку авторы модуля столкнулись
# с проблемами при выделении более чем 64КБ в некоторых
# операционных системах
mod_gzip_maximum_inmem_size 60000
# устанавливаем версию протокола, с которой будут отдаваться
# gzip-файлы на клиент. Настройка появилась в те времена, когда
# некоторые прокси неверно обрабатывали сжатый ответ. В версии 1.1
# протокола HTTP прокси обязан выставить заголовок Via,
# что позволяет определить, проходит ли запрос через прокси,
# в версии 1.0 такого заголовка нет, поэтому в те времена
# запросы версии 1.0 просто не сжимались
mod_gzip_min_http 1000
# исключаем известные проблемные случаи (IE до 6.0SP1)
# вообще-то старые версии Opera также содержат строку MSIE,
# но мы их отделять не будем, поскольку в Apache используется
# синтаксис POSIX Extended Regular Expression, а записать
# на этом языке два исключения в одном правиле невероятно трудно.
# Правило, которое записано ниже, может давать ложные срабатывания
# в некоторых ситуациях, но в реальности эти ситуации не встречаются
mod_gzip_item_exclude reqheader "User-agent: MSIE [456]([^S]V|[^S]V1|[^V]1|1[^;]|SV[^1]|[^SV1])+$"
# устанавливаем сжатие по умолчанию для файлов .html
mod_gzip_item_include file \.html$
# дополнительно сжимаем другие текстовые файлы с указанными
# MIME-типами
mod_gzip_item_include mime ^text/html$
mod_gzip_item_include mime ^text/plain$
mod_gzip_item_include mime ^httpd/unix-directory$
# отключаем сжатие для картинок (не дает никакого эффекта)
mod_gzip_item_exclude mime ^image/
# отключаем 'Transfer-encoding: chunked' для gzip-файлов, чтобы
# страница уходила на клиент одним куском
mod_gzip_dechunk Yes
# добавляем заголовок Vary для корректного распознавания браузеров,
# находящихся за локальными прокси-серверами
mod_gzip_send_vary On
</IfModule>
<IfModule mod_headers.c>
# запрещаем прокси-серверам кэшировать у себя сжатые версии файлов
<FilesMatch .*\.(html|txt)$>
Header set Cache-Control: private
</FilesMatch>
</IfModule>
Модуль для Apache 2.x, mod_deflate, по настройкам схож со своим
более ранним тезкой. Отличия: нет некоторых опций, которые вряд ли
мо гут быть названы сейчас актуальными, и разделение сжатия HTML-файлов
и файлов CSS/JS сделано удобнее.
Исходная версия конфигурационного файла этого модуля, опять же, взята из книги "Разгони свой сайт", здесь файл приводится с правками и расширенными комментариями.
Для управления этим модулем, как и в случае первого Apache,
необходим mod_setenvif, следует учитывать, что часть настроек этого модуля mod_deflate нельзя использовать внутри файлов .htaccess, только
внутри virtual host или на уровне глобальных настроек.
# Apache 2.x mod_deflate (необходим mod_setenvif) # с самого начала включаем gzip для текстовых файлов AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml # и для favicon.ico AddOutputFilterByType DEFLATE image/x-icon # также для CSS- и JavaScript-файлов AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE text/javascript AddOutputFilterByType DEFLATE application/x-javascript # далее устанавливаем максимальную степень сжатия (9) # и максимальный размер окна (15). Если сервер не очень мощный, # то уровень сжатия можно выставить в 1, размер файлов при этом # увеличивается примерно на 20%. DeflateCompressionLevel 9 DeflateWindowSize 15 DeflateBufferSize 32768 # отключаем сжатие для тех браузеров, у которых проблемы # с его распознаванием # выключаем сжатие для MSIE < 6SP1, исключаем # притворяющуюся IE "Оперу" BrowserMatch "MSIE [456]" no_gzip dont-vary BrowserMatch "SV1;" !no_gzip !dont-vary BrowserMatch "Opera" !no_gzip !dont-vary # Firefox < 3.5, проблемы с CSS/JS BrowserMatch "Firefox/[0-3]\." gzip-only-text/html BrowserMatch "Firefox/3\.[1-9]" !gzip-only-text/html # Chrome BrowserMatch "Chrome/2" gzip-only-text/html # все версии Safari BrowserMatch "Safari" gzip-only-text/html # Konqueror BrowserMatch "Konqueror" gzip-only-text/html # указываем прокси-серверам передавать заголовок User-Agent для # корректного распознавания сжатия Header append Vary User-Agent env=!dont-vary # запрещаем кэширование на уровне прокси-сервера для всех файлов, # для которых у нас выставлено сжатие, <FilesMatch .*\.(css|js|php|phtml|shtml|html|xml="")$> Header append Cache-Control: private </FilesMatch>
Достаточно популярный веб-сервер nginx (читается как "engine X") также имеет два модуля для управления сжатием: ngx_http_gzip_module для сжатия "на лету" и ngx_http_gzip_static_module для статически сжатых файлов.
# nginx, ngx_http_gzip_module и ngx_http_gzip_static_module
# включаем сжатие "на лету"
gzip on;
# типы файлов, который будут сжиматься, text/html указывать не нужно,
# этот тип сжимается по умолчанию
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/rss+xml text/javascript image/x-icon;
# минимальный размер сжимаемого контента
gzip_min_length 1000;
# максимальное сжатие
gzip_comp_level 9;
# минимальная версия протокола HTTP в запросе, при которой будет
# происходить сжатие
gzip_http_version 1.0;
# разрешает выдачу заголовка Vary
gzip_vary on;
# разрешить сжатие проксируемых ответов, у которых есть
# заголовки
# Expired, Cache-contol: no-cache, Cache-contol: no-store,
# Cache-contol: private или Authorization
gzip_proxied expired no-cache no-store private auth;
# отключаем сжатие для MSIE < 6.0SP1, с версии 0.8.11 эта маска
# не включает Internet Explorer 6.0SP2
gzip_disable msie6;
# к сожалению, у nginx нет способа запретить сжатие ответов
# какого-либо типа для обхода проблем в браузерах, но если
# у вас все файлы CSS и JavaScript оканчиваются, соответственно,
# на .css и .js, то можно ограничить их сжатие следующим способом
location ~* \.(css|js) {
# разрешаем отдавать вместо несжатого файла предварительно
# сжатый с постфиксом ".gz", если такой есть
gzip_static on;
# запрещаем сжатие файлов CSS и JS для проблемных браузеров
gzip_disable Firefox/([0-2]\.|3\.0);
gzip_disable Chrome/2;
gzip_disable Safari;
}
У веб-сервера lighttpd (lighty) также имеются модули (modcom-press, moddeflate),
поддерживающие сжатие контента. Причем поддерживаются не только методы gzip и
Модуль modcompress поддерживает схему со статическими сжатыми файлами, у него имеется кэш, куда складываются и где автоматически создаются сжимаемые модулем файлы.
Имя файла и его путь в кэше составляется из пути и имени запрашиваемого файла плюс имя метода сжатия и ETag. Соответственно, если меняется ETag, сервер создает файл с д ругим именем и отдает свежую копию.
Очистка кэша оставлена на усмотрение пользователя и может производиться, например, такой вот командой (удаление файлов, которые были модифицированы более 10 дней назад):
find /var/www/cache -type f -mtime +10 | xargs -r rm
По сравнению с остальными рассматриваемыми модулями mod_com-press имеет очень мало
возможностей для конфигурирования. Оптимизированный конфигурационный файл приведен ниже.
# Lighttpd, mod_compress
# включаем сжатие
server.modules += ("mod_compress")
# включаем методы, которые будем поддерживать,
# здесь не указан bzip2
# из-за проблем с прокси и deflate из-за проблем с Konqueror
compress.allowed-encodings = ("gzip")
# директорий, куда складываются кэшированные пресжатые файлы
compress.cache-dir = "/var/www/cache/"
compress.filetype = ("text/plain", "text/html", "text/xml", "application/xml", "application/rss+xml", "image/x-icon")
# не сжимать контент, размер которого больше, чем 500KiB
compress.max-filesize = 500000
# боремся с проблемами в браузерах, основываясь на URL
$HTTP["url"] =~ "\.(css|js)$" {
$HTTP["useragent"] != "Firefox/([0-2]\.|3\.0)" {
$HTTP["useragent"] != "Chrome/2|Konqueror" {
compress.filetype += ("text/css", "application/
x-javascript", "application/javascript",
"text/javascript")
}
}
}
$HTTP["useragent"] = ~ "MSIE [4-6]" {
$HTTP["useragent"] != "SV1|Opera" {
compress.filetype = ()
}
}
Модуль mod_deflate, который доступен в
версии 1.5.0 и выше (или как
# Lighttpd, mod_deflate
# включаем сжатие
server.modules += ("mod_deflate")
deflate.enabled = "enable"
# максимальное сжатие
deflate.compression-level = 9
deflate.mem-level = 9
deflate.window-size = 15
# включаем методы, которые будем поддерживать, здесь не указан
# bzip2 из-за проблем с прокси и deflate из-за проблем с Konqueror
deflate.allowed_encodings = ("gzip")
# минимальный размер ответа, который будем сжимать
deflate.min-compress-size = 1000
# из-за того, что в зависимости от браузера мы не может отключать
# сжатие JS и CSS, не включаем их сжатие вообще
deflate.mimetypes = ("text/plain", "text/html", "text/xml", "application/xml", "application/rss+xml", "image/x-icon")
# минимальный размер блока для сжатия
deflate.work-block-size = 512
# отключаем сжатие для старых IE
$HTTP["useragent"] = ~ "MSIE [4-6]" {
$HTTP["useragent"] != "SV1|Opera" {
deflate.enabled = "disable"
}
}
# боремся с проблемами в браузерах, основываясь на URL
$HTTP["url"] = ~ "\.(css|js)$" {
$HTTP["useragent"] != "Firefox/([0-2]\.|3\.0)" {
$HTTP["useragent"] != "Chrome/2|Konqueror|Safari" {
compress.filetype += ("text/css", "application/x-javascript", "application/javascript", "text/javascript")
}
}
}
Для иллюстрации, каким образом можно реализовать сжатие со стороны сервера, мы выбрали язык PHP, как наиболее распространенный и простой в понимании. Тем, кому язык знаком, знают, что в нем реализованы два встроенных механизма, позволяющие полностью автоматизировать процесс, но, к сожалению, они никак не помогают бороться с ошибками браузера.
<?php
class mod_compress
{
// настройки
// уровень сжатия
static public $deflatelevel = 9;
// минимальный размер ответа (в байтах), который будет
// сжат (0 — нет ограничений)
static public $minsize = 0;
// максимальный размер
static public $maxsize = 500000;
// внутренняя переменная — поддерживает ли браузер сжатие
static private $supported = null;
static public function init()
{
// поддерживает ли браузер gzip?
if (!self::issupportgzip()) {
return self::$supported = false;
}
// каков тип ответа, тело которого будет сжимать?
$type = self::detecttype();
if ($type == 'unknown') {
return self::$supported = false;
}
$ua = $_SERVER['HTTP_USER_AGENT'];
// сжатие браузер поддерживает, теперь нужно
// исключить браузер, который поддерживает его
// с ошибками — MSIE< 6.0SP2
if (preg_match('/MSIE [4-6](?:.(?!Opera|SV1))+/', $ua)) {
return self::$supported = false;
}
// если требуется сжимать CSS/JS, то нужно
// отфильтровать небезопасные браузеры
if (
$type == 'notsafe'
preg_match('@Chrome/2|Konqueror|Firefox/(?:[0-2]\.|3\.0)@', $ua)
) {
return self::$supported = false;
}
return self::$supported = true;
}
// посмотрим — поддерживает ли браузер gzip
static private function issupportgzip()
{
foreach (preg_split('/\s*,\s*/',
$_SERVER['HTTP_ACCEPT_ENCODING']) as $method) {
// некоторые браузеры указывают вес
// (предпочтения) методов сжатия, например,
// "bzip2;q=0.9, gzip;q=0.1"
// говорит о том, что браузер
// хотел бы, чтобы ему отдавали контент, сжатый
// методом bzip2,
// но он поддерживает и gzip
$method = explode(';', $method, 2);
// но так как мы поддерживаем только gzip, вес
// мы игнорируем
if ($method[0] == 'gzip' ||
$method[0] == 'x-gzip') {
return true;
}
}
return false;
}
// отделим "безопасные" типы от "небезопасных"
static private function detecttype()
{
// поддерживаются не всеми браузерами
$notsafe = array('text/css', 'text/javascript',
'application/javascript',
'application/x-javascript',
'text/x-js', 'text/ecmascript',
'application/ecmascript', 'text/vbscript',
'text/fluffscript');
// поддерживаются всеми браузерами
$safe = array('text/html', 'image/x-icon',
'text/plain',
'text/xml', 'application/xml',
'application/rss+xml');
foreach (headers_list() as $header) {
if (stripos($header, 'content-type') === 0) {
$header = preg_split('/\s*:\s*/', $header);
$type = strtolower($header[1]);
if (in_array($type, $safe)) return 'safe';
if (in_array($type, $notsafe))
return 'notsafe';
return 'unknown';
}
}
// в случае, если Content-type не задан, считаем,
// что это text/html
return 'safe';
}
// проверка ограничний на размер
static private function checksize($len)
{
if ($minsize $len < $minsize) return false;
if ($maxsize $len > $maxsize) return false;
return true;
}
// проверка, прошел ли запрос через прокси
static private function checkproxy()
{
// в версии HTTP 1.1 есть обязательный заголовок,
// который выставляет прокси, — Via, в HTTP/1.0
// такого признака нет
return $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0' || isset($_SERVER['HTTP_VIA']);
}
// обработчик, который решит, будет ли сжат контент,
// и сожмет его
static public function handler($content, $stage)
{
// проверим — нужно ли сжимать (не срабатывают ли
// ограничения на размер)
if ($content == '' ||
!self::checksize(strlen($content))) {
return $content;
}
if (self::$supported === null) {
self::init();
}
// браузер не поддерживается, высылаем оригинал
if (self::$supported === false) {
return $content;
}
// этот заголовок скажет прокси-серверу и браузеру,
// что возвращаемое нами будет иметь разное
// содержимое в зависимости от типа браузера
// и заголовка типа кодирования
header('Vary: User-agent, Content-encoding');
// запрещаем прокси сохранять содержимое,
// для обхода прокси, не справляющихся
// с кэшированием сжатого контента
if (self::checkproxy()) {
header('Cache-control: private');
}
return self::compress($content);
}
static public function compress($content)
{
// сжимаем текст gzip'ом
$content = gzencode ($content, $deflatelevel,FORCE_GZIP);
// не забываем отдать длину сжатого потока,
// это *очень* важно: некоторые браузеры не
// обрабатывают сжатый поток без указания длины
header('Content-length: ' . strlen($content));
// …и метод его кодирования
header('Content-encoding: gzip');
return $content;
}
}
ob_start(array('mod_compress', 'handler'));
// здесь вывод вашего скрипта
Как видим, задача не такая уж и сложная. Точкой входа в класс
является метод handler, который мы передаем функции ob_start, — она и
позаботится о том, чтобы весь дальнейший вывод попадал в нашу функцию.
Так как класс устанавливает заголовки, нужно, чтобы вызов ob_start
произошел в вашей программе как можно раньше, до любого вывода данных
в браузер.
Класс также можно доделать, чтобы поддержать метод bzip2 (нужно
проверять вхождения "bzip2", "x-bzip" или "bzip"), если ответ не является
проксируемым. Для сжатия bzip2 в PHP есть одноименный модуль. Его
также можно улучшить: заменить проверку наличия в заголовке gzip и
сжатие на вызов ob_gzhandler, но мы этого делать не стали намеренно чтобы продемонстрировать, как это делается, если читатель захочет
портировать код на другой язык.
Алгоритм работы класса следующий:
Браузер является настолько мощной платформой, поддерживающей такое количество разнообразных технологий, что одно и то же зачастую можно сделать несколькими способами, некоторые из которых красивы в силу своей изощренности, а другие — практичны, например, позволяют обойти ошибки браузеров.
Мы рассмотрим два метода реализации сжатия, которые можно использовать, если встроенное сжатие в клиентском браузере реализовано с ошибками. Первый метод — это сжатие через тег canvas, второе — сжатие JavaScript, реализованное на самом JavaScript.
Первый метод предложил Джейкоб Седелин в своем блоге "Nihilogic" (http://blog.nihilogic.dk/2008/05/compression-using-canvas-and-png.html); о реализации второго метода, возможно (тут трудно установить истину), впервые задумался один из авторов этой книги, реализовав в 2001 году в рамках проекта JUnix (http://junix.kzn.ru) простенькое сжатие, которое называлось jzip.
canvas — предложенный фирмой Apple тег, который на данный момент входит в HTML5. Он позволяет при помощи JavaScript создавать на ограниченном тегом участке растровые изображения.
Идея сжатия, реализуемого при помощи этого тега, проста — каждый байт сжимаемого содержимого представляется как точка изображения любого формата сжатия без потерь, поддерживаемого браузером (GIF, PNG). Это изображение запрашивается с сервера и подгружается в тег canvas методом drawImage (поддерживается браузерами Firefox 1.5 и выше, Safari 2.0 и выше, Opera 9.0 и выше, а также Google Chrome).
Как легко догадаться, из canvas изображение поточечно считывается, каждая точка представляется как символ, и полученная строка выполняется как JavaScript.
Уровень сжатия таким способом кода колеблется в условных пределах от 20 до 50%. Например, библиотека
Особенно привлекательно в этом методе то, что часть JavaScript, ответственная за получение кода на стороне клиента, очень небольшая:
var codeimg = new Image()
// когда картинка загрузится, вызовется эта функция
codeimg.onload = function () {
var code = '' // переменная, куда будет собираться код
var size = 119 // размер изображения (оно квадратное)
var canvas = document.createElement("canvas")
canvas.width = canvas.height =
canvas.style.width = canvas.style.height = size
var inner = canvas.getContext("2d")
// загружаем изображение в созданный нами CANVAS
inner.drawImage(codeimg)
// забираем содержимое и переводим его в символы
var data = inner.getImageData(0, 0, size, size)
for (var i = 0, len = data.length; i<len; i+="4"
if="" (data=""[i=""] >
0) code += String.fromCharCode(data[i])
}
eval(code)
}
// указываем URL изображения, где у нас хранится код
codeimg.src = 'image-with-our-code.png'
Серверная часть также очень проста, реализацию на PHP можно посмотреть в блоге автора метода, так же легко она реализуется на любом другом языке — в квадратное изображение, сторона которого равна квадратному корню из значения длины файла, в каждую точку ставится по коду символа из передаваемого контента.
Таким же методом можно паковать и CSS — в конце вместо eval нужно лишь создать в DOM тег<style> с соответствующим содержимым.
Само изображение, получающееся в результате, ничего интересного собой не представляет — обычный бинарный шум.
(рис 2.1) Библиотека Prototype (версия 1.6.0.2), сжатая до 30 Кб PNG (экономия 24%)У метода есть и недостатки. Во-первых, автор не использует цветовую составляющую для передачи каких-либо данных, и тут есть простор для экспериментов. Во-вторых, на текущий момент распаковка средних по размеру (200-500 Кб) данных занимает несколько секунд. В-третьих, метод поддерживается не всеми браузерами (например, не поддерживается IE). И в-четвертых, автор не позаботился о поддержке UTF-8 (что, впрочем, можно исправить).
Поскольку общее время, через которое скрипт будет доступен, равно сумме времени загрузки и времени, потраченного на его распаковку, нужно очень внимательно относиться к использованию этого метода — возможно, результатом его применения станет лишь увеличение времени ожидания.
Не исключено, что ситуацию можно исправить за счет использования цветовых компонент изображения. В этом случае размер передаваемых (а значит, и обрабатываемых скриптом) данных должен уменьшиться.
На клиентской стороне на JavaScript реализуют только распаковку данных, сжатие происходит на сервере и может быть реализовано на любом языке программирования.
Ничего необычного в реализации какого-либо алгоритма нет: JavaScript — такой же язык программирования, но есть специфика: браузеры на данный момент содержат недостаточно оптимизированные интерпретаторы, и это необходимо учитывать, иначе распаковка будет выполняться удручающе медленно.
Впрочем, на этом фронте есть улучшения. Новые интерпретаторы языков JavaScript браузеров Firefox, Safari и Google Chrome показывают впечатляющие результаты. Скажем, реализация на JavaScript сжатия
Впрочем, десятая "Опера", последняя на данный момент, показывает куда менее интересное время — полсекунды, а Internet Explorer 8.0 — 0,3 секунды.
В будущем, если там еще будут встречаться проблемы с реализацией gzip у браузеров, можно будет использовать реализации достаточно сложных методов сжатия на JavaScript, а в настоящем же приходится довольствоваться чем-то менее ресурсоемким.
Например, программа "Packer" Дина Эдвардса
Сжимаемый файл разбивается на слова, слова сортируются по частоте их употребления (сначала — наиболее употребительные), им присваиваются номера, которые кодируются алфавитом в 62 символа. Далее происходит замена — в коде слова заменяются их номерами в шестидятитид-вухричной системе. На клиенте осуществляется обратная замена.
Этот алгоритм работает достаточно быстро для того, чтобы накладные расходы на распаковку скрадывались улучшенным временем загрузки.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.