it-swarm.com.ru

Как заставить веб-браузер НЕ кэшировать изображения

Фон

Я пишу и использую очень простой инструмент управления контентом на основе CGI (Perl) для двух бесплатных веб-сайтов. Он предоставляет администратору веб-сайта HTML-формы для событий, где они заполняют поля (дата, место, заголовок, описание, ссылки и т.д.) И сохраняют их. В этой форме я разрешаю администратору загрузить изображение, связанное с событием. На странице HTML, отображающей форму, я также показываю предварительный просмотр загруженного изображения (тег HTML img).

Эта проблема

Проблема возникает, когда администратор хочет изменить картинку. Ему просто нужно нажать кнопку "Обзор", выбрать новое изображение и нажать "ОК". И это прекрасно работает.

Как только изображение загружено, мой серверный CGI обрабатывает загрузку и корректно перезагружает форму.

Проблема в том, что показанное изображение не обновляется. Старое изображение все еще отображается, хотя база данных содержит правильное изображение. Я сузил его до того факта, что ИЗОБРАЖЕНИЕ IS КЕШЕНО в веб-браузере. Если администратор нажимает кнопку RELOAD в Firefox/Explorer/Safari, все обновляется нормально, и новое изображение просто появляется.

Мое решение - не работает

Я пытаюсь контролировать кэш, написав инструкцию HTTP Expires с датой, которая была очень далека от прошлого.

Expires: Mon, 15 Sep 2003 1:00:00 GMT

Помните, что я нахожусь на административной стороне, и мне все равно, загружаются ли страницы немного дольше, потому что они всегда просрочены.

Но это тоже не работает.

Заметки

При загрузке изображения его имя не сохраняется в базе данных. Он переименован в Image.jpg (чтобы все было проще при его использовании). При замене существующего изображения новым, имя также не меняется. Просто меняется содержимое файла изображения.

Веб-сервер предоставляется хостинг-сервисом/провайдером. Он использует Apache.

Вопрос

Есть ли способ заставить веб-браузер НЕ кэшировать вещи с этой страницы, даже изображения?

Я жонглирую с возможностью на самом деле "сохранить имя файла" с базой данных. Таким образом, если изображение будет изменено, src тега IMG также изменится. Однако это требует много изменений по всему сайту, и я бы не стал этого делать, если у меня есть лучшее решение. Кроме того, это все равно не будет работать, если новое загруженное изображение будет иметь то же имя (скажем, изображение немного фотофотошоплено и повторно загружено).

111
Philibert Perusse

Армин Роначер имеет правильную идею. Проблема в том, что случайные строки могут сталкиваться. Я хотел бы использовать:

<img src="picture.jpg?1222259157.415" alt="">

Где "1222259157.415" - текущее время на сервере.
Генерируйте время с помощью Javascript с помощью performance.now() или Python с time.time()

170
epochwolf

Простое исправление: прикрепите случайную строку запроса к изображению:

<img src="foo.cgi?random=323527528432525.24234" alt="">

Что говорит HTTP RFC:

Cache-Control: no-cache

Но это не очень хорошо работает :)

45
Armin Ronacher

Я использую функция изменения времени файла PHP , например:

echo <img  src='Images/image.png?" . filemtime('Images/image.png') . "'  />";

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

18
Rick

Я хотел бы использовать:

<img src="picture.jpg?20130910043254">

где "20130910043254" - время изменения файла.

При загрузке изображения его имя не сохраняется в базе данных. Он переименован в Image.jpg (для упрощения использования). При замене существующего изображения новым, имя также не меняется. Просто меняется содержимое файла изображения.

Я думаю, что есть два типа простых решений: 1) те, которые приходят на ум первыми (простые решения, потому что их легко придумать), 2) те, с которыми вы сталкиваетесь после обдумывания вещей (потому что их легко использования). Очевидно, вы не всегда выиграете, если решите все обдумать. Но второй вариант, скорее всего, недооценен. Подумайте, почему php так популярен;)

15
x-yuri

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

HTML:

<img src="image.php?img=imageFile.jpg&some-random-number-262376" />

Автор сценария:

// PHP
if( isset( $_GET['img'] ) && is_file( IMG_PATH . $_GET['img'] ) ) {

  // read contents
  $f = open( IMG_PATH . $_GET['img'] );
  $img = $f.read();
  $f.close();

  // no-cache headers - complete set
  // these copied from [php.net/header][1], tested myself - works
  header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Some time in the past
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
  header("Cache-Control: no-store, no-cache, must-revalidate"); 
  header("Cache-Control: post-check=0, pre-check=0", false); 
  header("Pragma: no-cache"); 

  // image related headers
  header('Accept-Ranges: bytes');
  header('Content-Length: '.strlen( $img )); // How many bytes we're going to send
  header('Content-Type: image/jpeg'); // or image/png etc

  // actual image
  echo $img;
  exit();
}

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

7
Mindeh

Я НОВЫЙ кодер, но вот что я придумала, чтобы браузер не кэшировал и не держал взгляды с моей веб-камеры:

<meta Http-Equiv="Cache" content="no-cache">
<meta Http-Equiv="Pragma-Control" content="no-cache">
<meta Http-Equiv="Cache-directive" Content="no-cache">
<meta Http-Equiv="Pragma-directive" Content="no-cache">
<meta Http-Equiv="Cache-Control" Content="no-cache">
<meta Http-Equiv="Pragma" Content="no-cache">
<meta Http-Equiv="Expires" Content="0">
<meta Http-Equiv="Pragma-directive: no-cache">
<meta Http-Equiv="Cache-directive: no-cache">

Не уверен, что работает в каком браузере, но он работает для некоторых: IE: работает, когда веб-страница обновляется и когда веб-сайт повторно (без обновления). ХРОМ: работает только при обновлении веб-страницы (даже после повторного посещения). SAFARI и iPad: не работает, я должен очистить историю и веб-данные.

Есть идеи по поводу SAFARI/iPad?

6
Timmy T.

использовать Class = "NO-CACHE"

пример HTML:

<div>
    <img class="NO-CACHE" src="images/img1.jpg" />
    <img class="NO-CACHE" src="images/imgLogo.jpg" />
</div>

jQuery:

    $(document).ready(function ()
    {           
        $('.NO-CACHE').attr('src',function () { return $(this).attr('src') + "?a=" + Math.random() });
    });

javaScript:

var nods = document.getElementsByClassName('NO-CACHE');
for (var i = 0; i < nods.length; i++)
{
    nods[i].attributes['src'].value += "?a=" + Math.random();
}

Результат: src = "images/img1.jpg" => src = "images/img1.jpg? A = 0.08749723793963926"

5
Aref Rostamkhani

Я проверил все ответы в Интернете, и лучший из них, похоже, был: (на самом деле это не так)

<img src="image.png?cache=none">

вначале.

Однако, если вы добавите параметр cache = none (который является статическим словом "none" Word), это ничего не изменит, браузер все равно будет загружаться из кэша.

Решение этой проблемы было:

<img src="image.png?nocache=<?php echo time(); ?>">

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

Однако моя проблема была немного иной: я загружал на лету сгенерированное изображение php-диаграммы и управлял страницей с помощью параметров $ _GET. Я хотел, чтобы изображение считывалось из кеша, когда параметр GET URL-адреса остается прежним, и не кэшировалось при изменении параметров GET.

Чтобы решить эту проблему, мне нужно было хешировать $ _GET, но поскольку это массив, вот решение:

$chart_hash = md5(implode('-', $_GET));
echo "<img src='/images/mychart.png?hash=$chart_hash'>";

Edit:

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

echo "<img src='/images/mychart.png?hash=" . filemtime('mychart.png') . "'>";

filemtime () возвращает время изменения файла.

4
Tarik

При загрузке изображения его имя не сохраняется в базе данных. Он переименован в Image.jpg (для упрощения использования).

Измените это, и вы исправили свою проблему. Я использую временные метки, как и в решениях, предложенных выше: Image- <timestamp> .jpg

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

4
AmbroseChapel

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

У меня была очень похожая ситуация при загрузке изображений продуктов в административный бэкенд для сайта, похожего на магазин, и в моем случае я решил, что лучшим вариантом будет использование javascript для принудительного обновления изображения без использования каких-либо методов изменения URL-адреса других людей. уже упоминал здесь. Вместо этого я поместил URL-адрес изображения в скрытый IFRAME, который называется location.reload(true) в окне IFRAME, а затем заменил свое изображение на странице. Это вынуждает обновлять изображение не только на странице, на которой я сейчас работаю, но и на любых последующих страницах, которые я посещаю, без необходимости запоминания клиентом или сервером каких-либо параметров строки запроса URL или идентификатора фрагмента.

Я написал код для этого в своем ответе здесь .

2
Doin

Добавить отметку времени <img src="picture.jpg?t=<?php echo time();?>">

всегда будет давать вашему файлу случайное число в конце и останавливать его кеширование

2
BritishSam

Я предполагаю, что оригинальный вопрос касается изображений, хранящихся с некоторой текстовой информацией. Итак, если у вас есть доступ к текстовому контексту при генерации src = ... url, рассмотрите возможность хранения/использования CRC32 байтов изображения вместо бессмысленной случайной или временной метки. Затем, если отображается страница с большим количеством изображений, будут загружены только обновленные изображения. В конце концов, если сохранение CRC невозможно, его можно вычислить и добавить к URL во время выполнения.

1
jbw

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

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

1
user7375

С моей точки зрения отключить кэширование изображений - плохая идея. Совсем.

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

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

В моем случае это служба отдыха, которая возвращает изображение и присоединяет ETag в ответ. Сервис сохраняет хеш всех файлов, если файл изменен, хеш обновляется. Он отлично работает во всех современных браузерах. Да, для его реализации требуется время, но оно того стоит.

Единственное исключение - это фавиконы. По некоторым причинам это не работает. Я не мог заставить браузер обновить свой кеш со стороны сервера. ETags, Cache Control, Expires, заголовки Pragma, ничего не помогло.

В этом случае, добавление некоторого параметра random/version в URL, кажется, является единственным решением.

1
Alexandr

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

Для этого вы должны отслеживать ресурсы, которые могут потребоваться для синхронизации, и либо использовать xhr для проверки изображений с помощью динамической строки запроса, либо создавать изображение во время выполнения с помощью src, используя динамическую строку запроса. Затем используйте механизм широковещания, чтобы уведомить все компоненты веб-страниц, которые используют ресурс, для обновления, чтобы использовать ресурс с динамической строкой запроса, добавленной к его URL-адресу.

Наивный пример выглядит так:

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

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

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="mobile-web-app-capable" content="yes" />        
    <title>Resource Synchronization Test</title>
    <script>
function sync() {
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {            
            var images = document.getElementsByClassName("depends-on-resource");

            for (var i = 0; i < images.length; ++i) {
                var image = images[i];
                if (image.getAttribute('data-resource-name') == 'resource.bmp') {
                    image.src = 'resource.bmp?i=' + new Date().getTime();                
                }
            }
        }
    }
    xhr.open('GET', 'resource.bmp', true);
    xhr.send();
}
    </script>
  </head>
  <body>
    <img class="depends-on-resource" data-resource-name="resource.bmp" src="resource.bmp"></img>
    <button onclick="sync()">sync</button>
  </body>
</html>
0
Dmitry