кэширование файлов на стороне клиента - PullRequest
16 голосов
/ 11 августа 2010

Если я правильно понимаю, броузер кэширует изображения, файлы JS и т. Д. На основе имени файла.Поэтому существует опасность, что если один такой файл будет обновлен (на сервере), браузер будет использовать вместо этого кэшированную копию.

Обходной путь для этой проблемы - переименовать все файлы (как часть сборки),таким образом, что имя файла включает в себя MD5-хэш его содержимого, например,

foo.js -> foo_AS577688BC87654.js
me.png -> me_32126A88BC3456BB.png

Однако, помимо переименования самих файлов, все ссылки на эти файлы должны быть изменены.Например, тег, такой как <img src="me.png"/>, должен быть изменен на <img src="me_32126A88BC3456BB.png"/>.

Очевидно, что это может быть довольно сложно, особенно если учесть, что ссылки на эти файлы могут динамически создаваться в коде на стороне сервера.

Конечно, одно из решений - полностью отключить кэширование в браузере (и любые кэши между сервером и браузером) с использованием заголовков HTTP.Однако отсутствие кэширования создаст собственный набор проблем.

Есть ли лучшее решение?

Спасибо, Дон

Ответы [ 11 ]

16 голосов
/ 31 августа 2010

Лучшим решением, по-видимому, является использование имен файлов версий путем добавления времени последнего изменения.

Вы можете сделать это следующим образом: добавить правило перезаписи в конфигурацию Apache, например:

RewriteRule ^(.+)\.(.+)\.(js|css|jpg|png|gif)$ $1.$3

Это перенаправит любой «версионный» URL на «нормальный».Идея состоит в том, чтобы сохранить ваши имена файлов такими же, но извлечь выгоду из кэша.Решение добавить параметр в URL не будет оптимальным для некоторых прокси, которые не кэшируют URL с параметрами.

Тогда вместо записи:

<img src="image.png" />

Просто вызовите PHPфункция:

<img src="<?php versionFile('image.png'); ?>" />

с versionFile () выглядит следующим образом:

function versionFile($file){
    $path = pathinfo($file);
    $ver = '.'.filemtime($_SERVER['DOCUMENT_ROOT'].$file).'.';
    echo $path['dirname'].'/'.str_replace('.', $ver, $path['basename']);
}

И это все!Браузер запросит image.123456789.png, Apache перенаправит его в image.png, так что вы получите выгоду от кэша во всех случаях и не будете иметь никаких устаревших проблем, без необходимости беспокоиться о версии файлов..

Подробное описание этой техники вы можете увидеть здесь: http://particletree.com/notebook/automatically-version-your-css-and-javascript-files/

9 голосов
/ 11 августа 2010

Почему бы просто не добавить номер строки запроса в "version" и обновлять версию каждый раз?

foo.js -> foo.js? Version = 5

Там все еще есть немногово время сборки работайте над обновлением номеров версий, но имена файлов менять не нужно.

6 голосов
/ 11 августа 2010

Переименование ваших ресурсов - путь, хотя мы используем номер сборки и встраиваем его в имя файла вместо хеша MD5

foo.js -> foo.123.js

, поскольку это означает, что все ваши ресурсы могут быть переименованыдетерминированным образом и разрешается во время выполнения.

Затем мы используем пользовательские элементы управления для создания ссылок на ресурсы при загрузке страницы на основе номера сборки, который хранится в настройке приложения.

3 голосов
/ 02 сентября 2010

Я бы предложил использовать кэширование ETags в этой ситуации, см. http://en.wikipedia.org/wiki/HTTP_ETag. Затем вы можете использовать хэш в качестве этаг.Запрос будет по-прежнему отправляться для каждого ресурса, но браузер будет загружать только те элементы, которые изменились с момента последней загрузки.

Ознакомьтесь с документацией по веб-серверу / платформе о том, как правильно использовать etags, большинство достойных платформвстроенная поддержка.

3 голосов
/ 01 сентября 2010

Мы следовали шаблону, аналогичному PJP, используя Rails и Nginx.

Мы хотели, чтобы изображения аватара пользователя кэшировались в браузере, но при изменении аватара нам нужно было как можно скорее сделать кэш недействительным.

Мы добавили метод к модели аватара, чтобы добавить метку времени к имени файла:

return "/images/#{sourcedir}/#{user.login}-#{self.updated_at.to_s(:flat_string)}.png"

Во всех местах кода, где использовались аватары, мы ссылались на этот метод, а не на URL. В конфигурации Nginx мы добавили это переписывание:

rewrite "^/images/avatars/(.+)-[\d]{12}.png"    /images/avatars/$1.png;
rewrite "^/images/small-avatars/(.+)-[\d]{12}.png"      /images/small-avatars/$1.png;

Это означало, что если файл изменился, изменился его URL в HTML, поэтому браузер пользователя сделал новый запрос на файл. Когда запрос достиг Nginx, он был переписан на простое имя файла.

2 голосов
/ 02 сентября 2010

Это действительно проблема, только если ваш веб-сервер устанавливает заголовок «Expires» на далекое будущее (установка что-то вроде ExpiresDefault "access plus 10 years" в вашей конфигурации Apache). В противном случае браузер выполнит условное GET на основе измененного времени и / или Etag. Вы можете проверить, что происходит на вашем сайте, используя веб-прокси или такое расширение, как Firebug (на панели «Сеть»). В вашем вопросе не указано, как настроен ваш веб-сервер и какие заголовки он отправляет со статическими файлами.

Если вы не устанавливаете заголовок Expires на далекое будущее, вам не нужно делать ничего особенного. Ваш веб-сервер обычно отлично обрабатывает условные GET для статических файлов, основываясь на времени последнего изменения. Если вы устанавливаете заголовок Expires на далекое будущее, тогда да, вам нужно добавить какую-либо версию к имени файла, например ваш вопрос, а другие ответы уже упоминались.

2 голосов
/ 02 сентября 2010

Большинство современных браузеров проверяют заголовок if-updated-Since всякий раз, когда кэшируемый ресурс находится в HTTP-запросе.Однако не все браузеры поддерживают заголовок if-updated-Since.

Существует три способа принудительно заставить браузер загрузить кэшированный ресурс.

Параметр 1 Создайте строку запроса с версией #.src="script.js?ver=21".Недостатком является то, что многие прокси-серверы не кэшируют ресурс с помощью строк запроса.Также требуется обновление всего сайта для изменений.

Вариант 2 Создание системы именования для ваших файлов src="script083010.js".Однако недостатком варианта 1 является то, что для этого также требуются обновления на уровне сайта при каждом изменении файла.

Вариант 3 Возможно, самое элегантное решение, просто настройте заголовки кэширования: last-изменен и истекает на вашем сервере.Основным недостатком этого является то, что пользователям, возможно, придется восстанавливать ресурсы, потому что они истекли, но никогда не менялись.Кроме того, последний измененный заголовок не работает, когда контент обслуживается с нескольких серверов.

Вот несколько ресурсов для проверки: Yahoo Google AskApache.com

1 голос
/ 07 февраля 2012

ETag, по-видимому, обеспечивают решение для этого ...

Согласно http://httpd.apache.org/docs/2.0/mod/core.html#fileetag, мы можем настроить браузер на генерацию ETag по размеру файла (вместо времени / индекса / и т. Д.) Это поколение должно быть постоянным для нескольких серверов.

Просто включите его в (/etc/apache2/apache2.conf)

FileETag Size

и тебе должно быть хорошо!

Таким образом, вы можете просто ссылаться на свои изображения как <img src='/path/to/foo.png' /> и при этом использовать все возможности HTTP-кэширования.

1 голос
/ 02 сентября 2010

Возможно, вы захотите проверить подход, используемый плагином Grails "uiperformance", который вы можете найти здесь . Он делает много вещей, о которых вы упоминаете, но автоматизирует их (задает длительность срока действия, а затем увеличивает номера версий при изменении файлов).

Так что, если вы используете Grails, вы получаете этот материал бесплатно. Если нет - возможно, вы можете позаимствовать используемые методы.

Также - заимствовано из страницы пользовательского интерфейса, - прочитайте следующие 14 правил .

1 голос
/ 02 сентября 2010

Я считаю, что комбинация решений работает лучше всего:

  1. Установка дат истечения срока действия кэша для каждого типа ресурса (изображения, страницы и т. Д.) Специально для этого ресурса, например:

    • Ваши статические страницы "О нас", "Контакты" и т. Д., Вероятно, не будут меняться чаще, чем несколько раз в год, поэтому вы можете легко разместить на этих страницах время кеширования.
    • Изображения, используемые на этих страницах, могут иметь вечное время кеширования, так как вы более склонны заменить изображение, чем изменить его.
    • Аватар изображения могут иметь время истечения дня.
  2. Некоторым ресурсам необходимо изменить даты в своих именах. Например, аватары, сгенерированные изображения и т. П.

  3. Некоторые вещи никогда не должны быть кешами, новыми страницами, пользовательским контентом и т. Д. В этих случаях вы должны кэшировать на сервере, но никогда на стороне клиента.

В конце концов, вам нужно тщательно продумать каждый тип ресурса, чтобы определить, какое время кэширования использовать браузеру, и всегда быть консервативным, если вы не уверены. Вы можете увеличить время позже, но гораздо сложнее что-то распаковать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...