Кеширование ресурсов (картинок / js / css ...) в браузере
Зависимости
- Нет зависимостей
Базовое описание компонента
-
Компонент выполняет две основных функции:
- Осуществляет корректный вывод файла в выходной поток, вместе с соответствующим формату файла хидером.
- Через хидеры организовывает кеширование ресурсов (картинки, js, css, zip и пр.) в браузере пользователя.
Отдача файла (с одновременным кешированием в браузере пользователя), происходит следующим образом:
BrowserDataCache::OutFile(BASEPATH . 'i/image/def_logo.png');
Чтобы организовать автоматическо кеширование обычных ресурсов сайта (находящихся в папках i, tmp, upl), нужно в файле core/init.php заменить строчку GetQuery() на BrowserDataCacheFinder::TryFind(GetQuery()).
При этом нужно настроить сервер, чтобы происходило перенаправление вызова так, чтобы вместо www.example.com/i/image/sample.jpg, вызывался www.example.com/index.php?q=i/image/sample.jpg
Для веб-сервера Apache правило для перенаправления задается через модуль mod_rewrite, в файле .htaccess в корне сайта:
RewriteEngine on RewriteBase / RewriteCond $1 !^(index\.php|robots\.txt|favicon\.ico|sitemap\.xml) RewriteRule ^(.*)$ /index.php?q=$1 [L,QSA]
Полное описание принципа работы
Обычно мы обращаемся к ресурсам сайта (находящимся в папках i, tmp, upl) напрямую, через url следующего вида:
www.example.com/i/image/sample.jpg
Чтобы осущестить выдачу и кеширование таких файлов через BrowserDataCache, нам нужно организовать обращение к ним через php скрипт.
Первый способ: можно поменять url на:
www.example.com/index.php?q=i/image/sample.jpg
Это будет легко сделать, если для формирования ссылок на файлы вы используете функцию Root("i/image/sample.jpg").
После соответствующей модификации она будет выглядеть следующим образом:
function Root($uri = '') { $dir = SITE_IN_DIR ? (SITE_IN_DIR . '/') : ''; return SITE_ROOT . "{$dir}?q={$uri}"; }Данный способ имеет право на жизнь, но менее предпочтителен. Если есть возможность, то следует придерживаться подходов, которые позволяют сохранить обратную совместимость с обычным обращением к файлу. То есть в идеале нам желательно оставить простой и понятный url к файлам вида www.example.com/i/image/sample.jpg не тронутым.
Второй способ: можно настроить сервер так, чтобы происходило перенаправление запроса на другой url.
То есть при обращении к www.example.com/i/image/sample.jpg, должен на самом деле вызываться www.example.com/index.php?q=i/image/sample.jpg.
Для веб-сервера Apache правило для перенаправления задается через модуль mod_rewrite, в файле .htaccess в корне сайта.
Пример:
# Запретить получение списка файлов Options -Indexes # При запросе к директории, будет вызван файл index.php DirectoryIndex index.php # Включить механизм преобразований RewriteEngine on # RewriteBase отрабатывает после всех RewriteRule. # Если после всех преобразований в RewriteRule, путь останется относительным (без адреса сайта и / в начале), то RewriteBase допишет себя слева. RewriteBase / # От пути "example.com/i/image/sample.jpg" в RewriteRule и RewriteCond будет передано только "i/image/sample.jpg" # Определяет выполнять в итоге преобразование в RewriteRule или нет. То есть для указанных файлов преобразование не будет выполнено. RewriteCond $1 !^(index\.php|robots\.txt|favicon\.ico|sitemap\.xml) # Строка будет отдана в index.php через GET переменную q RewriteRule ^(.*)$ /index.php?q=$1 [L,QSA]
Есть ещё третий способ: через 404 ошибку на сервере, однако это очень спецефичный метод и он не является темой данной статьи.
Использование и настройка
Теперь, обращение к файлу происходит через php скрипт и мы можем использовать BrowserDataCache.
-
Для чего может понадобиться выдача файла через php скрипт:
- Когда файл генерируется на ходу (с возможным складированием в кеш). Это может быть уменьшеное preview картинки или картинка для каптчи;
- Когда нужно выполнить определенный код перед выдачей ресурса. Допустим организовать сбор статистики или проверить права доступа;
- Если нужно выполнить кеширование файла в браузере пользователя (с помощью отправки соответствующих заголовочников);
- Нет возможности отдать ресурс другим методом.
Суть работы BrowserDataCache в том, что мы просто вызываем BrowserDataCache::OutFile() для нужного файла:
BrowserDataCache::OutFile(BASEPATH . 'i/image/file.png');Дальше скрипт выполняться не будет, а выходной поток будет передан файл с соответствующими хидерами (которые автоматом определятся сами по типу файла).
Помните, что нам не обязательно обращаться к существующему файлу. Мы, к примеру, можем создать скрипт src/get_def_logo.php, в котором будет лишь одна строчка:
BrowserDataCache::OutFile(BASEPATH . 'i/image/def_logo.png');То есть обращаясь по адресу www.site.com/get_def_logo мы будем видеть содержимое файла def_logo.png
Чтобы сработало обычное кеширование для существующих на сайте файлов/ресурсов, по сути нужно взять лишь текущую строку запроса (функция GetQuery()), проверить соответствует ли она существующему ресурсу и если да, то отдать этот реусурс.
Для решения этой задачи существует BrowserDataCacheFinder, который устанавливается вместе с BrowserDataCache.
Для того чтобы он заработал, нужно в файле core/init.php заменить строчку GetQuery() на
BrowserDataCacheFinder::TryFind(GetQuery()).
Если ресурс не будет найден, то BrowserDataCacheFinder не будет ничего делать, а просто передаст управление дальше.
Настройки библиотеки BrowserDataCache хранятся в файле core/config/browserdatacache.php.
Одним из важнейших параметров конфига является $g_config['browserdatacache_allow_dirs'], который указывает из каких папок
разрешено запрашивать кешируемые ресурсы. Здесь перечисляются только необходимые директории, создавая таким образом дополнительный уровень защиты.
Сроки кеширования и некоторые другие параметы настраиваются через константы в файле lib/BrowserDataCache.php
Другие способы кеширования
Важно помнить, что кеширование файла совершенно не обязательно делать через php скрипт и BrowserDataCache. В этом случае вызывается php скрипт, а это увеличивает нагрузку на сервер. Если есть возможность, то желательно делать кеширование средставами сервера. Для сервера Apache это можно сделать через файл .htaccess и существует несколько вариантов того, как это можно сделать.
Вариант через модуль mod_expires:
# Установить кеширеование на некоторые типы файлов <IfModule mod_expires.c> ExpiresActive On ExpiresDefault "access 7 days" ExpiresByType application/javascript "access plus 1 year" ExpiresByType text/javascript "access plus 1 year" ExpiresByType text/css "access plus 1 year" ExpiresByType text/html "access plus 7 day" ExpiresByType text/x-javascript "access 1 year" ExpiresByType image/gif "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType image/jpg "access plus 1 year" ExpiresByType image/x-icon "access 1 year" ExpiresByType application/x-shockwave-flash "access 1 year" </IfModule>
Вариант через модуль mod_headers:
<IfModule mod_headers.c> # 30 дней <filesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|swf|css|js|xml|txt)$"> Header set Cache-Control "max-age=2592000, public" </filesMatch> </IfModule>
Вариант модуль mod_expires с использованием ETags:
# Установить ETags на файлы сервера с указанным расширением FileETag MTime Size <IfModule mod_expires.c> <filesmatch "\.(jpg|gif|png|css|js)$"> ExpiresActive on # Когда проверить ETags для этого файла в следующий раз (дата доступа к файлу +1 год) ExpiresDefault "access plus 1 year" </filesmatch> </IfModule> # Запрет отдачи HTTP-заголовков некоторым браузерам <IfModule mod_setenvif.c> BrowserMatch "MSIE" force-no-vary BrowserMatch "Mozilla/4.[0-9]{2}" force-no-vary </IfModule>
Если сервер поддерживает gzip сжатие, то можно добавить следующий код:
# Включить gzip сжатие для text, html, javascript, css, xml <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE application/xml AddOutputFilterByType DEFLATE application/xhtml+xml AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/x-javascript AddOutputFilterByType DEFLATE text/plain AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE text/css SetOutputFilter DEFLATE </IfModule>