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


124

Задний план

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

Эта проблема

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

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

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

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

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

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

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

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

Ноты

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

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

Вопрос

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

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


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

Ответы:


186

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

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

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


32
Одним из важных дополнений является то, что вы никогда не можете заставить браузер что-либо делать. Все, что вы можете сделать, это сделать дружеские предложения. Фактически следовать этим предложениям - дело браузера и пользователя. Браузер может игнорировать это, или пользователь может изменить настройки по умолчанию.
Джоэл Кохорн,

8
Джоэл, тебе лучше было бы добавить это в свой ответ.
epochwolf

1
Просто мысль, и слишком поздно приходить сюда на вечеринку, но поскольку вы контролируете изменение изображения, возможно, было бы лучше переименовать изображение по мере его обновления, а не добавлять строку запроса. Итак: 1222259157.jpg например, вместо picture.jpg? 1222259157. Таким образом, он обновляется и повторно кэшируется при повторном посещении.
danjah

43
Вместо того, чтобы добавлять время сервера, которое полностью предотвратит кеширование, почему бы не добавить время последнего изменения файла после '?'. Таким образом, изображение будет кэшироваться в обычном режиме до следующего изменения.
Doin

3
За последний комментарий Doin нужно проголосовать! Умное кеширование действительно важно, зачем кому-то скачивать один и тот же файл снова и снова?
f.arcarese

46

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

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

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

Cache-Control: no-cache

Но это не так хорошо :)


1
как дать Cache-Control: no-cache в обычном теге html <img>?
P Satish Patro

Должен ли он быть в заголовке ответа?
P Satish Patro

22

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

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

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


Отличный совет. У вас есть и кеширование, и изображение, повторно отправленное клиенту, если время его изменения изменяется (перезаписывается или изменяется).
Василис Лурдас 09

см. здесь другой вариант stackoverflow.com/questions/56588850/…
drooh

Absolute это удалось. Отличный помощник по чаевым.
Хуррам Шейх

Отлично ! Еще один совет: <img src = "images / image.png? <? Php echo filemtime ('images / image.jpg')?>">
Рика Гургель

16

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

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

где «20130910043254» - время модификации файла.

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

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


1
+1 Я думаю, что это намного лучшая идея, чем другие ответы, потому что, используя время последней модификации, ваш сервер не будет пытаться передавать одно и то же изображение несколько раз в один и тот же браузер, если в изображении ничего не изменилось. Есть небольшие накладные расходы на извлечение последнего времени записи изображения каждый раз, когда есть запрос, но для большого изображения это лучше, чем возвращать изображение каждый раз, и когда изображение изменится, у пользователя будет новое. Спасибо за это приятное маленькое дополнение.
Samuel

Ну, можно сохранить время модификации вместе с путем к изображению в базе данных. Так что накладные расходы могут быть еще менее значительными. С другой стороны, если это изображения, которые являются частью исходного кода, о котором мы говорим, можно также кэшировать время их модификации. Путем создания сценария с указанием времени модификации изображений (например images.php). Этот сценарий необходимо повторно создавать при каждой фиксации и устраняет накладные расходы на определение времени модификации файлов.
x-yuri

@ x-yori, поэтому я предпочитаю хранить это обновленное поле в базе данных, а не запускать filemtime stackoverflow.com/questions/56588850/…
drooh

9

используйте 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"


Просто, элегантно и не требует рендеринга на стороне сервера. ОТЛИЧНО!
Ahi Tuna

7

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

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 изображения, но поскольку мы хотим быть пуленепробиваемыми ...


Ваше решение - хорошее, за исключением того, что Pragma не является заголовком ответа.
Писквор покинул здание

1
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32, похоже, говорит иначе.
Grizly

6

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

<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: работает, когда веб-страница обновляется и когда веб-сайт повторно посещается (без обновления). ХРОМ: работает только при обновлении веб-страницы (даже после повторного посещения). САФАРИ и iPad: не работает, мне нужно очистить историю и веб-данные.

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


4

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

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

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


4

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

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

вначале.

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

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

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

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

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

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

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

Редактировать :

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

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

filemtime () получает время модификации файла.


2
filemtimeРешение лучше и потому , что md5занимает много вычислительной мощности.
oriadam

2
Потрачены дни на попытки заставить приложение на основе Chromium перестать кэшировать изображения. Функция? Nocache with time echo решила проблему. Спасибо!
Woody

2

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

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

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


2

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

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


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

1

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

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


1

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


Или используйте время последнего изменения изображения вместо CRC, даже лучше!
Doin

1

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

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

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

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

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

В этом случае, кажется, единственное решение - добавить какой-нибудь параметр random / version в url.


1

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

Для этого вы должны отслеживать ресурсы, которые могут нуждаться в синхронизации, и либо использовать 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

Вы должны использовать уникальные имена файлов. Как это

<img src="cars.png?1287361287" alt="">

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

<img src="cars.png?2020-02-18" alt="">

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

<img src="cars.png?<?php echo time();?>" alt="">

Однако это все еще не эффективно. Причина: кеш браузера ... Последний, но наиболее эффективный метод - это Native JAVASCRIPT. Этот простой код находит все изображения с классом «NO-CACHE» и делает изображения почти уникальными. Поместите это между тегами скрипта.

var items = document.querySelectorAll("img.NO-CACHE");
for (var i = items.length; i--;) {
    var img = items[i];
    img.src = img.src + '?' + Date.now();
}

ИСПОЛЬЗОВАНИЕ

<img class="NO-CACHE" src="https://upload.wikimedia.org/wikipedia/commons/6/6a/JavaScript-logo.png" alt="">

РЕЗУЛЬТАТ (ы) как это

https://example.com/image.png?1582018163634
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.