Як змусити веб-браузер НЕ кешувати зображення


124

Фон

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

Проблема

Проблема виникає, коли адміністратор хоче змінити зображення. Йому просто доведеться натиснути кнопку «переглянути», вибрати нову картинку і натиснути ОК. І це чудово працює.

Після завантаження зображення мій задній CGI обробляє завантаження та перезавантажує форму належним чином.

Проблема полягає в тому, що показане зображення не оновлюється. Старе зображення все ще відображається, навіть якщо база даних містить правильне зображення. Я звузив це до того, що Зображення КЕШУЄТЬСЯ у веб-браузері. Якщо адміністратор натисне кнопку RELOAD у Firefox / Explorer / Safari, все добре оновлюється, і нове зображення просто з’являється.

Моє рішення - не працює

Я намагаюся керувати кешем, написавши інструкцію HTTP Expires з датою, дуже далекою в минулому.

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

Пам'ятайте, що я перебуваю з адміністративної сторони, і мені дуже не важливо, чи завантажуються сторінки трохи більше часу, оскільки вони завжди закінчуються.

Але це теж не працює.

Примітки

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

Веб-сервер надається службою хостингу / ISP. Тут використовується Apache.

Питання

Чи є спосіб змусити веб-браузер НЕ кешувати речі з цієї сторінки, навіть зображення?

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


Я думаю, що це питання може бути перероблене, щоб видалити весь "тепер марний контекст", який я вклав туди, коли писав його. Я думаю, що заголовок правильний, і верхня відповідь також. Але питання було написане спочатку, щоб забезпечити багато місця для відповідей багатьох різновидів, і я не міг очікувати такого простого рішення. Тому питання дещо заплутане для людей, які приїжджають туди, щоб отримати відповідь.
Філіберт Персе

Відповіді:


186

Армін Ронахер має правильну думку. Проблема в тому, що випадкові рядки можуть стикатися. Я б використав:

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

Де "1222259157.415" - поточний час на сервері.
Створіть час за допомогою Javascript з performance.now()або Python за допомогоюtime.time()


32
Важливим доповненням є те, що ви ніколи не можете змусити браузер нічого робити. Все, що ви можете зробити, це зробити дружні пропозиції. За веб-переглядачем і користувачем реально дотримуватися цих пропозицій. Веб-переглядач може ігнорувати це, або користувач може змінити значення за замовчуванням.
Joel Coehoorn

8
Джоеле, тобі було б краще додати це у власній відповіді.
epochwolf

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

43
Замість того, щоб додати час сервера, що взагалі не дозволить кешувати, чому б не додати останній час зміни файлу після "?". Таким чином, зображення буде кешовано нормально до наступного зміни.
Доїнь

3
Останній коментар Doin повинен бути схвалений! Розумне кешування дуже важливо, чому комусь доведеться завантажувати один і той же файл?
f.arcarese

46

Просте виправлення: Додайте до зображення випадковий рядок запиту:

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

Що говорить HTTP RFC:

Cache-Control: no-cache

Але це не так добре працює :)


1
як надати Cache-Control: no-кеш у звичайному тезі html <img>?
P

Чи потрібно входити в заголовок відповіді?
P

22

Я використовую функцію зміни часу файлу PHP , наприклад:

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

Якщо ви змінюєте зображення, то використовується нове зображення, а не кешоване, завдяки різній зміненій часовій позначці.


Чудова порада. Ви маєте і кешування, і зображення повторно надіслані клієнту, якщо його змінений час змінюється (перезаписується чи змінюється).
Василіс Лурдас

дивіться тут інший варіант stackoverflow.com/questions/56588850 / ...
drooh

Абсолют прибив її. Чудовий підручник.
Хурам Шайх

Ідеально! Лише додаткова порада: <img src = "images / image.png? <? Php echo filemtime ('images / image.jpg')?>">
Rica Gurgel

16

Я б використав:

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

де "20130910043254" - час модифікації файлу.

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

Я думаю, що існує два типи простих рішень: 1) ті, які приходять на думку спочатку (прості рішення, оскільки їх легко придумати), 2) ті, з якими ви закінчуєтесь після продумування речей (тому що їх легко зробити використання). Мабуть, ви не завжди отримаєте користь, якщо вирішили все продумати. Але я вважаю, що другий варіант є досить заниженим. Подумайте, чому phpтак популярний;)


1
+1 Я думаю, що це набагато краща ідея, ніж інші відповіді, оскільки, використовуючи час модифікації las, ваш сервер не намагатиметься надати одне і те ж зображення кілька разів одному і тому ж браузеру, коли в зображенні нічого не змінилося. Існує трохи накладних витрат, щоб витягнути час останнього запису зображення кожного разу, коли з’являється запит, але для великого зображення краще, ніж щоразу повертати зображення, і коли зображення буде змінено, у користувача з’явиться нове. Дякую за це приємне маленьке доповнення.
Самуїл

Що ж, можна зберігати час модифікації разом із шляхом до зображення в базі даних. Тож накладні витрати можуть бути навіть менш значними. З іншого боку, якщо це зображення, які є частиною вихідного коду, про який ми говоримо, можна також кешувати їх модифікації. Створюючи сценарій із часом модифікації зображень (наприклад, images.php). Цей скрипт повинен бути регенерований кожен фіксатор і виключає накладні витрати на визначення часу модифікації файлів.
x-yuri

@ Х-yoріте тому я вважаю за краще зберігати це оновлене поле в базі даних , а не працює 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"


Простий, елегантний і не вимагає візуалізації на стороні сервера. НІШЕ!
Ахі Туна

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 повинно бути достатнім, але оскільки ми хочемо бути куленепробивними ..


Ваше - хороше рішення, за винятком того, що Прагма не є заголовком відповідей.
Пісквор вийшов з будівлі

1
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32, здається, говорить інакше.
Grizly

6

Я НОВИЙ Coder, але ось що я придумав, щоб перешкодити браузеру кешувати та утримувати перегляди веб-камери:

<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?


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, щоб параметр був динамічним і не кешував, він працював.

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

Щоб вирішити цю проблему, мені потрібно було хеш $ _GET, але оскільки це масив, тут є рішення:

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

Редагувати :

Хоча вищезазначене рішення працює чудово, іноді ви хочете обслуговувати кешовану версію ДО ТОМУ, щоб файл був змінений. (з вищезазначеним рішенням він повністю вимикає кеш для цього зображення) Отже, для подання кешованого зображення з браузера UNTIL відбувається зміна використання файлу зображення:

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

filemtime () отримує час модифікації файлу.


2
filemtimeРішення краще і тому , що md5займає багато обчислювальної потужності.
oriadam

2
Провели дні, намагаючись отримати додаток на основі хрому, щоб зупинити кешування зображень. «Nocache з часом відлуння вирішило питання. Дякую!
Вуді

2

Ваша проблема полягає в тому, що, незважаючи на Expires:заголовок, ваш веб-переглядач повторно використовує копію зображення в пам’яті до того, як воно було оновлено, а не навіть перевіряючи кеш-пам'ять.

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

Я розмістив якийсь код, щоб це зробити у своїй відповіді тут .


2

Додати позначку часу <img src="picture.jpg?t=<?php echo time();?>">

завжди надасть вашому файлу випадкове число в кінці і зупинить його кешування


Варто зауважити, що реалізація цього рішення накладає стрес на сервер за запитом і не обов’язково працює, оскільки браузер може кешувати сторінку php, яка містить час, сформований під час первинного запиту, який отримує кешування. Це надійно працюватиме лише у тому випадку, якщо на сторінці також міститься динамічний рядок запитів.
Дмитро

1

З можливістю погано поводитись прозорими проксі-сервера між вами та клієнтом, єдиний спосіб повністю гарантувати, що зображення не будуть кешовані, - це надати їм унікальний урі, щось на зразок тегування часової позначки як рядок запиту або як частина запиту шлях.

Якщо ця часова мітка відповідає останньому часу оновлення зображення, ви можете кешувати, коли вам потрібно, і подавати нове зображення в потрібний час.


1

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


Або використовуйте останній час зображення, замість CRC, ще краще!
Doin

1

З моєї точки зору, відключення кешування зображень - погана ідея. Зовсім.

Основна проблема тут - як змусити браузер оновлювати зображення, коли воно було оновлено на стороні сервера.

Знову ж таки, з моєї особистої точки зору, найкращим рішенням є відключення прямого доступу до зображень. Замість цього звертайтесь до зображень через серверний фільтр / сервлет / інші подібні інструменти / послуги.

У моєму випадку це послуга відпочинку, яка повертає зображення та додає ETag у відповідь. Служба зберігає хеш усіх файлів, якщо файл змінено, хеш оновлюється. Він чудово працює у всіх сучасних браузерах. Так, для його здійснення потрібен час, але воно того варте.

Єдиний виняток - фавікони. З якихось причин це не працює. Не вдалося змусити браузер оновити кеш-пам'ять з боку сервера. ETags, кеш-контроль, закінчується, заголовки прагми, нічого не допомогло.

У цьому випадку додавання якогось випадкового / версійного параметра в 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.