Оновіть зображення новим у тому самому URL-адресі


333

Я переходжу до посилання на своєму веб-сайті, яке надаватиме нове зображення кожного разу, коли доступ до нього.

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

var newImage = new Image();
newImage.src = "http://localhost/image.jpg";

function updateImage()
{
if(newImage.complete) {
    document.getElementById("theText").src = newImage.src;
    newImage = new Image();
    number++;
    newImage.src = "http://localhost/image/id/image.jpg?time=" + new Date();
}

    setTimeout(updateImage, 1000);
}

Заголовки, як FireFox бачить їх:

HTTP/1.x 200 OK
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Transfer-Encoding: chunked
Content-Type: image/jpeg
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Server: Microsoft-HTTPAPI/1.0
Date: Thu, 02 Jul 2009 23:06:04 GMT

Мені потрібно змусити оновити саме це зображення на сторінці. Будь-які ідеї?


Відповіді:


350

Спробуйте додати кеш-руйдера в кінці URL-адреси:

newImage.src = "http://localhost/image.jpg?" + new Date().getTime();

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


26
Це не дуже вдале рішення, оскільки воно змоче кеші (як локальні, так і вище за течією). У відповіді Айя є кращий спосіб впоратися з цим.
Тгр

1
Крім того, повторне відображення одного і того ж зображення в іншому місці, без "вимикача кеша" пізніше, як і раніше показує стару кешовану версію (принаймні, у firefox) обох? і # :(
T4NK3R

3
Ви можете зробити ще менше коду за допомогою цього:'image.jpg?' + (+new Date())
Лев Лукомський

4
Є Date.now()для цього
vp_arth

2
Чому б і ніMath.random()
Gowtham Gopalakrishnan

227

Я бачив чимало варіацій у відповідях, як це зробити, тому подумав, що я їх підсумую тут (плюс додати четвертий метод власного винаходу):


(1) Додайте до URL-адреси унікальний параметр запиту кеш-пам'яті, наприклад:

newImage.src = "image.jpg?t=" + new Date().getTime();

Плюси: 100% надійний, швидкий і простий для розуміння та реалізації.

Мінуси: взагалі обходить кешування, тобто непотрібні затримки та використання смуги частот, коли зображення не змінюється між переглядами. Потенційно заповнює кеш браузера (і будь-які проміжні кеші) багатьма, багатьма копіями абсолютно одного зображення! Також потрібна зміна URL-адреси зображення.

Коли використовувати: Використовуйте, коли зображення постійно змінюється, наприклад, для прямого каналу веб-камери. Якщо ви використовуєте цей метод, переконайтеся, що ви самі обслуговуєте зображення із Cache-control: no-cacheзаголовками HTTP !!! (Часто це можна встановити за допомогою файлу .htaccess). Інакше ви будете поступово заповнювати кеші зі старими версіями зображення!


(2) Додайте параметр запиту до URL-адреси, яка змінюється лише тоді, коли файл, наприклад:

echo '<img src="image.jpg?m=' . filemtime('image.jpg') . '">';

(Це код на стороні сервера PHP, але тут важливим моментом є лише те, що до імені файлу додається запит? M = [остання зміна часу файлу].

Плюси: 100% надійний, швидкий і простий для розуміння та впровадження, і прекрасно зберігає переваги кешування.

Мінуси: Потрібно змінити URL-адресу зображення. Крім того, трохи більше роботи для сервера - він повинен отримати доступ до часу, зміненого файлом. Також потрібна інформація на стороні сервера, тому не підходить для суто клієнтського рішення для перевірки оновленого зображення.

Коли використовувати: Коли ви хочете кешувати зображення, але, можливо, доведеться час від часу оновлювати їх на сервері, не змінюючи саме ім'я файлу. І коли ви можете легко переконатися, що правильний запит додається до кожного екземпляра зображення у вашому HTML.


(3) Подавати зображення з заголовком Cache-control: max-age=0, must-revalidate, і додайте унікальний Memcache -busting фрагмент ідентифікатора в URL, наприклад:

newImage.src = "image.jpg#" + new Date().getTime();

Ідея тут полягає в тому, що заголовок кеш-керування розміщує зображення в кеш-пам’яті браузера, але одразу позначає їх застарілими, так що кожен раз, коли вони повторно відображаються, браузер повинен перевірити на сервері, чи не змінилися вони. Це гарантує, що HTTP-кеш браузера завжди повертає останню копію зображення. Однак браузери часто повторно використовують копію зображення в пам'яті, якщо вони є, і навіть не перевіряють кеш HTTP у цьому випадку. Щоб запобігти цьому, використовується ідентифікатор фрагмента: Порівняння зображень у пам'яті srcвключає ідентифікатор фрагмента, але його позбавляють перед тим, як запитувати кеш HTTP. (Так, наприклад, image.jpg#Aі вони image.jpg#Bможуть бути відображені із image.jpgзапису HTTP-кешу браузера, алеimage.jpg#Bніколи не відображатиметься з використанням даних, збережених у пам'яті, з моменту image.jpg#Aостаннього відображення).

Плюси: правильно використовує механізми кешування HTTP та використовує кешовані зображення, якщо вони не змінилися. Працює для серверів, які задавляють запит, доданий до URL-адреси статичного зображення (оскільки сервери ніколи не бачать ідентифікаторів фрагментів - вони призначені лише для власного використання браузерів).

Мінуси: покладається на дещо сумнівну (або, принаймні, погано задокументовану) поведінку браузерів щодо зображень з ідентифікаторами фрагментів у їхніх URL-адресах (однак я успішно протестував це в FF27, Chrome33 та IE11). Чи все-таки надсилає на сервер запит на повторну перевірку для кожного перегляду зображень, що може бути надмірним, якщо зображення змінюються лише рідко і / або затримка є великою проблемою (оскільки потрібно чекати відповіді на повторну перевірку навіть тоді, коли кешоване зображення все ще добре) . Потрібна зміна URL-адрес зображення.

Коли використовувати: Використовуйте, коли зображення можуть часто змінюватися або потребувати періодичного оновлення клієнтом без участі сценарію на стороні сервера, але там, де ви все ще хочете переваги кешування. Наприклад, опитування живої веб-камери, яка оновлює зображення нерегулярно кожні кілька хвилин. Крім того, використовуйте замість (1) або (2), якщо ваш сервер не дозволяє запити на URL-адреси статичних зображень.


(4) Примусово оновлюйте певне зображення за допомогою Javascript, спочатку завантажуючи його в приховане, <iframe>а потім викликаючи location.reload(true)рамки iframe contentWindow.

Етапи:

  • Завантажте оновлене зображення в прихований кадр. Це лише крок налаштування - за бажанням можна зробити заздалегідь фактичне оновлення. Навіть не важливо, чи не вдасться завантажити зображення на цьому етапі!

  • Як тільки це буде зроблено, видаліть усі копії цього зображення на ваших сторінках або будь-яких місцях у будь-яких вузлах DOM (навіть сторонніх, що зберігаються у змінних javascript). Це необхідно, тому що браузер може інакше відображати зображення із застарілої копії пам’яті (особливо це робить IE11): Вам потрібно забезпечити очищення всіх копій пам’яті , перш ніж оновити кеш HTTP. Якщо інший код JavaScript працює асинхронно, вам може знадобитися також запобігти тому, щоб цей код створював нові копії оновленого зображення.

  • Дзвінок iframe.contentWindow.location.reload(true). В trueсилах перепускний кешу, перезарядка безпосередньо з сервера і перезапис існуючих кешованих копій.

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

Для зображень з тим же доменом ви можете завантажити зображення безпосередньо в рамку iframe. Для зображень між доменами вам замість цього потрібно завантажити HTML-сторінку зі свого домену, яка містить зображення в <img>тегу, інакше при спробі дзвінка ви отримаєте помилку "Відмовлено у доступі" iframe.contentWindow.reload(...).

Плюси: працює так само, як функція image.reload (), яку ви хочете, щоб DOM мав! Дозволяє нормально кешувати зображення (навіть із наступними термінами придатності, якщо вони потрібні, тим самим уникаючи частого оновлення). Дозволяє оновити певне зображення, не змінюючи URL-адреси цього зображення на поточній сторінці або на будь-яких інших сторінках, використовуючи лише код на стороні клієнта.

Мінуси: покладається на Javascript. Не на 100% гарантовано працювати належним чином у кожному браузері (я проте успішно протестував це в FF27, Chrome33 та IE11). Дуже складний щодо інших методів.

Коли користуватися: Коли у вас є колекція в основному статичних зображень, які ви хочете кешувати, але вам все одно потрібно мати можливість періодично їх оновлювати і отримувати негайний візуальний відгук про те, що оновлення відбулося. (Особливо, коли просто оновити всю сторінку браузера не буде працювати, як, наприклад, у деяких веб-додатках, побудованих на AJAX). І коли методи (1) - (3) є нездійсненними, оскільки (з будь-якої причини) ви не можете змінити всі URL-адреси, які потенційно можуть відображати зображення, яке потрібно оновити. (Зауважте, що за допомогою цих 3-х методів зображення буде оновлено, але якщо інша сторінка потім намагатиметься відобразити це зображення без відповідного запиту рядка або ідентифікатора фрагмента, натомість воно може відображати старішу версію).

Деталі реалізації цього казкового надійного та гнучкого способу наведені нижче:

Припустимо, що ваш веб-сайт містить пустий 1x1 піксель .gif на шляху URL-адреси /img/1x1blank.gif, а також має такий однорядковий скрипт PHP (необхідний лише для застосування примусового оновлення до зображень між доменними , і його можна переписати будь-якою мовою скриптів на стороні сервера звичайно) в URL-адресі /echoimg.php:

<img src="<?=htmlspecialchars(@$_GET['src'],ENT_COMPAT|ENT_HTML5,'UTF-8')?>">

Тоді ось реалістична реалізація того, як ви можете зробити все це в Javascript. Це виглядає трохи складно, але коментарів багато, і важливою функцією є просто forceImgReload () - перші два просто порожні та непорожні зображення, і вони повинні бути розроблені для ефективної роботи з вашим власним HTML, тому кодуйте їх як найкраще працює для вас; значна частина ускладнень у них може бути непотрібною для вашого веб-сайту:

// This function should blank all images that have a matching src, by changing their src property to /img/1x1blank.gif.
// ##### You should code the actual contents of this function according to your page design, and what images there are on them!!! #####
// Optionally it may return an array (or other collection or data structure) of those images affected.
// This can be used by imgReloadRestore() to restore them later, if that's an efficient way of doing it (otherwise, you don't need to return anything).
// NOTE that the src argument here is just passed on from forceImgReload(), and MAY be a relative URI;
// However, be aware that if you're reading the src property of an <img> DOM object, you'll always get back a fully-qualified URI,
// even if the src attribute was a relative one in the original HTML.  So watch out if trying to compare the two!
// NOTE that if your page design makes it more efficient to obtain (say) an image id or list of ids (of identical images) *first*, and only then get the image src,
// you can pass this id or list data to forceImgReload() along with (or instead of) a src argument: just add an extra or replacement parameter for this information to
// this function, to imgReloadRestore(), to forceImgReload(), and to the anonymous function returned by forceImgReload() (and make it overwrite the earlier parameter variable from forceImgReload() if truthy), as appropriate.
function imgReloadBlank(src)
{
  // ##### Everything here is provisional on the way the pages are designed, and what images they contain; what follows is for example purposes only!
  // ##### For really simple pages containing just a single image that's always the one being refreshed, this function could be as simple as just the one line:
  // ##### document.getElementById("myImage").src = "/img/1x1blank.gif";

  var blankList = [],
      fullSrc = /* Fully qualified (absolute) src - i.e. prepend protocol, server/domain, and path if not present in src */,
      imgs, img, i;

  for each (/* window accessible from this one, i.e. this window, and child frames/iframes, the parent window, anything opened via window.open(), and anything recursively reachable from there */)
  {
    // get list of matching images:
    imgs = theWindow.document.body.getElementsByTagName("img");
    for (i = imgs.length; i--;) if ((img = imgs[i]).src===fullSrc)  // could instead use body.querySelectorAll(), to check both tag name and src attribute, which would probably be more efficient, where supported
    {
      img.src = "/img/1x1blank.gif";  // blank them
      blankList.push(img);            // optionally, save list of blanked images to make restoring easy later on
    }
  }

  for each (/* img DOM node held only by javascript, for example in any image-caching script */) if (img.src===fullSrc)
  {
    img.src = "/img/1x1blank.gif";   // do the same as for on-page images!
    blankList.push(img);
  }

  // ##### If necessary, do something here that tells all accessible windows not to create any *new* images with src===fullSrc, until further notice,
  // ##### (or perhaps to create them initially blank instead and add them to blankList).
  // ##### For example, you might have (say) a global object window.top.blankedSrces as a propery of your topmost window, initially set = {}.  Then you could do:
  // #####
  // #####     var bs = window.top.blankedSrces;
  // #####     if (bs.hasOwnProperty(src)) bs[src]++; else bs[src] = 1;
  // #####
  // ##### And before creating a new image using javascript, you'd first ensure that (blankedSrces.hasOwnProperty(src)) was false...
  // ##### Note that incrementing a counter here rather than just setting a flag allows for the possibility that multiple forced-reloads of the same image are underway at once, or are overlapping.

  return blankList;   // optional - only if using blankList for restoring back the blanked images!  This just gets passed in to imgReloadRestore(), it isn't used otherwise.
}




// This function restores all blanked images, that were blanked out by imgReloadBlank(src) for the matching src argument.
// ##### You should code the actual contents of this function according to your page design, and what images there are on them, as well as how/if images are dimensioned, etc!!! #####
function imgReloadRestore(src,blankList,imgDim,loadError);
{
  // ##### Everything here is provisional on the way the pages are designed, and what images they contain; what follows is for example purposes only!
  // ##### For really simple pages containing just a single image that's always the one being refreshed, this function could be as simple as just the one line:
  // ##### document.getElementById("myImage").src = src;

  // ##### if in imgReloadBlank() you did something to tell all accessible windows not to create any *new* images with src===fullSrc until further notice, retract that setting now!
  // ##### For example, if you used the global object window.top.blankedSrces as described there, then you could do:
  // #####
  // #####     var bs = window.top.blankedSrces;
  // #####     if (bs.hasOwnProperty(src)&&--bs[src]) return; else delete bs[src];  // return here means don't restore until ALL forced reloads complete.

  var i, img, width = imgDim&&imgDim[0], height = imgDim&&imgDim[1];
  if (width) width += "px";
  if (height) height += "px";

  if (loadError) {/* If you want, do something about an image that couldn't load, e.g: src = "/img/brokenImg.jpg"; or alert("Couldn't refresh image from server!"); */}

  // If you saved & returned blankList in imgReloadBlank(), you can just use this to restore:

  for (i = blankList.length; i--;)
  {
    (img = blankList[i]).src = src;
    if (width) img.style.width = width;
    if (height) img.style.height = height;
  }
}




// Force an image to be reloaded from the server, bypassing/refreshing the cache.
// due to limitations of the browser API, this actually requires TWO load attempts - an initial load into a hidden iframe, and then a call to iframe.contentWindow.location.reload(true);
// If image is from a different domain (i.e. cross-domain restrictions are in effect, you must set isCrossDomain = true, or the script will crash!
// imgDim is a 2-element array containing the image x and y dimensions, or it may be omitted or null; it can be used to set a new image size at the same time the image is updated, if applicable.
// if "twostage" is true, the first load will occur immediately, and the return value will be a function
// that takes a boolean parameter (true to proceed with the 2nd load (including the blank-and-reload procedure), false to cancel) and an optional updated imgDim.
// This allows you to do the first load early... for example during an upload (to the server) of the image you want to (then) refresh.
function forceImgReload(src, isCrossDomain, imgDim, twostage)
{
  var blankList, step = 0,                                // step: 0 - started initial load, 1 - wait before proceeding (twostage mode only), 2 - started forced reload, 3 - cancelled
      iframe = window.document.createElement("iframe"),   // Hidden iframe, in which to perform the load+reload.
      loadCallback = function(e)                          // Callback function, called after iframe load+reload completes (or fails).
      {                                                   // Will be called TWICE unless twostage-mode process is cancelled. (Once after load, once after reload).
        if (!step)  // initial load just completed.  Note that it doesn't actually matter if this load succeeded or not!
        {
          if (twostage) step = 1;  // wait for twostage-mode proceed or cancel; don't do anything else just yet
          else { step = 2; blankList = imgReloadBlank(src); iframe.contentWindow.location.reload(true); }  // initiate forced-reload
        }
        else if (step===2)   // forced re-load is done
        {
          imgReloadRestore(src,blankList,imgDim,(e||window.event).type==="error");    // last parameter checks whether loadCallback was called from the "load" or the "error" event.
          if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
        }
      }
  iframe.style.display = "none";
  window.parent.document.body.appendChild(iframe);    // NOTE: if this is done AFTER setting src, Firefox MAY fail to fire the load event!
  iframe.addEventListener("load",loadCallback,false);
  iframe.addEventListener("error",loadCallback,false);
  iframe.src = (isCrossDomain ? "/echoimg.php?src="+encodeURIComponent(src) : src);  // If src is cross-domain, script will crash unless we embed the image in a same-domain html page (using server-side script)!!!
  return (twostage
    ? function(proceed,dim)
      {
        if (!twostage) return;
        twostage = false;
        if (proceed)
        {
          imgDim = (dim||imgDim);  // overwrite imgDim passed in to forceImgReload() - just in case you know the correct img dimensions now, but didn't when forceImgReload() was called.
          if (step===1) { step = 2; blankList = imgReloadBlank(src); iframe.contentWindow.location.reload(true); }
        }
        else
        {
          step = 3;
          if (iframe.contentWindow.stop) iframe.contentWindow.stop();
          if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
        }
      }
    : null);
}

Потім, щоб примусити оновити зображення, розташоване в тому самому домені, що і ваша сторінка, ви можете просто зробити:

forceImgReload("myimage.jpg");

Щоб оновити зображення з іншого місця (крос-домен):

forceImgReload("http://someother.server.com/someimage.jpg", true);

Більш досконалою програмою може бути перезавантаження зображення після завантаження нової версії на ваш сервер, підготовка початкової стадії процесу перезавантаження одночасно з завантаженням, щоб мінімізувати видиму затримку перезавантаження для користувача. Якщо ви робите завантаження через AJAX, а сервер повертає дуже простий масив JSON [успіх, ширина, висота], ваш код може виглядати приблизно так:

// fileForm is a reference to the form that has a the <input typ="file"> on it, for uploading.
// serverURL is the url at which the uploaded image will be accessible from, once uploaded.
// The response from uploadImageToServer.php is a JSON array [success, width, height]. (A boolean and two ints).
function uploadAndRefreshCache(fileForm, serverURL)
{
  var xhr = new XMLHttpRequest(),
      proceedWithImageRefresh = forceImgReload(serverURL, false, null, true);
  xhr.addEventListener("load", function(){ var arr = JSON.parse(xhr.responseText); if (!(arr&&arr[0])) { proceedWithImageRefresh(false); doSomethingOnUploadFailure(...); } else { proceedWithImageRefresh(true,[arr[1],ar[2]]); doSomethingOnUploadSuccess(...); }});
  xhr.addEventListener("error", function(){ proceedWithImageRefresh(false); doSomethingOnUploadError(...); });
  xhr.addEventListener("abort", function(){ proceedWithImageRefresh(false); doSomethingOnUploadAborted(...); });
  // add additional event listener(s) to track upload progress for graphical progress bar, etc...
  xhr.open("post","uploadImageToServer.php");
  xhr.send(new FormData(fileForm));
}

Підсумкове зауваження: Хоча ця тема стосується зображень, вона потенційно стосується й інших файлів чи ресурсів. Наприклад, запобігання використанню застарілих скриптів або файлів css або, можливо, навіть оновлення оновлених документів PDF (використовуючи (4), лише якщо встановлено для відкриття в браузері). Метод (4) може вимагати деяких змін у вищевказаному JavaScript у цих випадках.


Мені подобається ідея методу 4, але ви не можете завантажувати зовнішній вміст у програму Iframe, чи не так? В даний час я використовую метод 3 в односторінковому веб-додатку, але мені не подобається те, що вам доведеться перезавантажувати всю сторінку, щоб отримати нове зображення, навіть якщо HTML шаблону буде перезавантажений.
Emilios1995

@Emilios: ... Крім того, я не розумію ваш коментар про необхідність перезавантажувати всю сторінку. Обидва способи (3) і (4) можуть бути реалізовані в JavaScript на стороні клієнта, не перезавантажуючи нічого, крім одного зображення, яке ви оновлюєте. Для методу (3), який би означав просто використання JavaScript, щоб змінити властивість 'src' вашого зображення з (наприклад) image.jpg#123на image.jpg#124(або що завгодно, поки біт після зміни "#"). Чи можете ви пояснити, що це ви перезавантажуєте, і чому?
Зробіть

@Emilios: Ви дійсно можете завантажувати зовнішній (міждоменний) вміст у iframe ... але ви не можете потім отримати доступ до нього contentWindowчерез javascript, щоб зробити reload(true)виклик, що є найважливішою частиною методу ... так ні, метод (4 ) не працюватиме для міждоменного вмісту. Добре помічений; Я оновлю "Мінуси", щоб включити це.
Зробіть

@Emilios: На жаль, я не буду: я зрозумів, що просте виправлення (тепер включене до моєї відповіді) дозволяє йому працювати і для міждоменних зображень (за умови, що ви можете розмістити сценарій на сервері на своєму сервері).
Зробіть

@pseudosavant: На жаль, я це помічаю лише ~ 17 місяців пізніше, але мені шкода, що вам потрібно сказати, ваші зміни до мого коду були сильно порушені. (Справедливо кажучи, я також не думаю, що код зворотного дзвінка, який у мене був, був правильним). Зараз я детально переписав частину (4), і пояснення, і код. Ваш попередній код ніколи не бланкував зображення (тому він може виходити з ладу дивними способами, особливо в IE, особливо якщо зображення було показано в декількох місцях), але ще гірше, що він також видалив iframe відразу після початку повного перезавантаження, що, ймовірно, означало, що це працювати лише з перервами або зовсім не працювати. Вибачте!
Зробіть

185

Як альтернатива ...

newImage.src = "http://localhost/image.jpg?" + new Date().getTime();

...здається, що...

newImage.src = "http://localhost/image.jpg#" + new Date().getTime();

... достатньо, щоб обдурити кеш браузера, не обминаючи кеш-версій, за умови, що ви повернули правильні Cache-Controlзаголовки. Хоча ви можете використовувати ...

Cache-Control: no-cache, must-revalidate

... ви втрачаєте переваги If-Modified-Sinceабо If-None-Matchзаголовки, тож щось на зразок ...

Cache-Control: max-age=0, must-revalidate

... повинен перешкоджати браузеру повторно завантажувати все зображення, якщо воно фактично не змінилося. Тестували та працювали на IE, Firefox та Chrome. Прикро це виходить з ладу на Safari, якщо ви не використовуєте ...

Cache-Control: no-store

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

Оновлення (2014-09-28): сьогодні, схоже Cache-Control: no-store, потрібно і для Chrome.


1
Чудово! Після багато часу, намагаючись завантажити мої веб-зображення, які завантажувалися із затримкою, я щойно вирішив це, застосувавши ваше рішення (з "#", використовуючи "?" Для мене не працює). Дуже дякую!!!
користувач304602

18
Тут задіяні ДВІ кеші: Там є звичайний кеш HTTP браузера та кеш пам'яті зображень, який він нещодавно відображав. Цей останній кеш пам'яті індексується повним srcатрибутом, тому додавання унікального ідентифікатора фрагмента гарантує, що зображення не буде просто витягнуто з пам'яті. Але ідентифікатори фрагментів не надсилаються як частина HTTP-запитів, тому звичайний кеш HTTP буде використовуватися як звичайний. Ось чому ця техніка працює.
Doin

Є кілька кеш-заголовків. насправді я не знаю англійської мови дуже добре, скажіть, будь ласка, чи варто використовувати яку ?! Я хочу чогось, що не кешує лише фотографію, яку змінено (як капчу), і кешувати інші речі. так Cache-Control: max-age=0, must-revalidateце добре для мене?
Шафізаде

Це не працює для мене. Єдине, що в моєму випадку відрізняється, це те, що у мене є URL-адреса до дії контролера, який отримує img з db. У мене були інші аргументи для дії контролера, тому я додав його як "...... & convert = true & t =" + new Date (). GetTime (); та "...... & convert = true #" + нова дата (). getTime () ;. Чи є щось, що я роблю неправильно?
shaffooo

1
Щоб уникнути накладних витрат на створення об'єкта та / або виклику методу, ви можете використовувати newImage.src = "http://localhost/image.jpg#" + i++;
наростаюче

7

Після створення нового зображення ви видаляєте старе зображення з DOM та заміняєте його новим?

Ви можете захоплювати нові зображення кожен виклик updateImage, але не додавати їх на сторінку.

Існує ряд способів зробити це. Щось подібне спрацювало б.

function updateImage()
{
    var image = document.getElementById("theText");
    if(image.complete) {
        var new_image = new Image();
        //set up the new image
        new_image.id = "theText";
        new_image.src = image.src;           
        // insert new image and remove old
        image.parentNode.insertBefore(new_image,image);
        image.parentNode.removeChild(image);
    }

    setTimeout(updateImage, 1000);
}

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


3

Одна з відповідей полягає в хакейному додаванні параметрів запиту на отримання, як було запропоновано.

Краща відповідь - виділити пару додаткових опцій у своєму HTTP-заголовку.

Pragma: no-cache
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Cache-Control: no-cache, must-revalidate

Зазначаючи дату раніше, браузер не буде кешований. Cache-Controlбуло додано в HTTP / 1.1, і тег, необхідний для повторного оновлення, вказує на те, що проксі-сервіси ніколи не можуть обслуговувати старе зображення навіть при пом'якшувальних обставинах, і Pragma: no-cacheце насправді не потрібно для сучасних сучасних браузерів / кешів, але може допомогти з деякими поважними зламаними старими реалізаціями.


3
Це звучало так, як би це спрацювало, але воно все одно показує те саме зображення, навіть із хаками. Я додам інформацію щодо заголовка до питання.
QueueHammer

Я щойно помітив, що ви знову і знову оновлюєте один і той же тег img. Браузер, ймовірно, виявляє, коли ви переходите до встановлення src, що src не змінився, і тому його не турбує оновити. (Оскільки ця перевірка відбувається на рівні DOM і не має нічого спільного з цільовим). Що станеться, якщо ви додасте "?" + номер - до URL-адреси завантажуваного зображення?
Edward KMETT

3

У мене була вимога: 1) не можна додавати жодне ?var=xxзображення до зображення; 2) воно повинно працювати між доменом

Мені дуже подобається варіант №4 у цій відповіді одним, але:

  • у нього проблеми з надійною роботою з кросдоменом (і це вимагає торкання коду сервера).

Мій швидкий і брудний спосіб:

  1. Створіть прихований кадр
  2. Завантажте на нього поточну сторінку (так, цілу сторінку)
  3. iframe.contentWindow.location.reload(true);
  4. Повторно встановіть джерело зображення для себе

Ось

function RefreshCachedImage() {
    if (window.self !== window.top) return; //prevent recursion
    var $img = $("#MYIMAGE");
    var src = $img.attr("src");
    var iframe = document.createElement("iframe");
    iframe.style.display = "none";
    window.parent.document.body.appendChild(iframe);
    iframe.src = window.location.href;
    setTimeout(function () {
        iframe.contentWindow.location.reload(true);
        setTimeout(function () {
            $img.removeAttr("src").attr("src", src);
        }, 2000);
    }, 2000);
}

Так, я знаю, setTimeout ... Ви повинні змінити це на належні події завантаження.


3
<img src='someurl.com/someimage.ext' onload='imageRefresh(this, 1000);'>

Потім нижче в деяких javascript

<script language='javascript'>
 function imageRefresh(img, timeout) {
    setTimeout(function() {
     var d = new Date;
     var http = img.src;
     if (http.indexOf("&d=") != -1) { http = http.split("&d=")[0]; } 

     img.src = http + '&d=' + d.getTime();
    }, timeout);
  }
</script>

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


2

Що я в кінцевому підсумку робив, це те, що сервер відображав будь-який запит на зображення в цій директорії до джерела, яке я намагався оновити. Потім у мене таймер додав номер на кінець імені, щоб DOM побачив це як нове зображення та завантажив його.

Напр

http://localhost/image.jpg
//and
http://localhost/image01.jpg

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

var newImage = new Image();
newImage.src = "http://localhost/image.jpg";
var count = 0;
function updateImage()
{
    if(newImage.complete) {
        document.getElementById("theText").src = newImage.src;
        newImage = new Image();
        newImage.src = "http://localhost/image/id/image" + count++ + ".jpg";
    }
    setTimeout(updateImage, 1000);
}

8
Це матиме ту саму проблему кешування декількох копій зображення, як і рішення запитів (Paolo та деякі інші), і вимагає змін на сервері.
TomG

2

function reloadImage(imageId)
{
   path = '../showImage.php?cache='; //for example
   imageObject = document.getElementById(imageId);
   imageObject.src = path + (new Date()).getTime();
}
<img src='../showImage.php' id='myimage' />

<br/>

<input type='button' onclick="reloadImage('myimage')" />


3
будь ласка, поясніть ОП, як і чому це допомагає замість просто вставлення коду
номістично

Я не думаю, що ../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit)це дійсне ім'я файлу ... Принаймні, це те, що він намагається завантажити ...
ByteHamster

змінити path='../showImage.php';наpath='../showImage.php?';
BOOMik

2
document.getElementById("img-id").src = document.getElementById("img-id").src

встановити власний src як свій src.


1

Спробуйте скористатися марною рядком запитів, щоб зробити його унікальним URL-адресою:

function updateImage()
{
    if(newImage.complete) {
        document.getElementById("theText").src = newImage.src;
        newImage = new Image();
        number++;
        newImage.src = "http://localhost/image.jpg?" + new Date();
    }

    setTimeout(updateImage, 1000);
}

Додав WQS до коду і перевірив, що запит приймається, і що браузер бачить відповідь, що надходить з адреси + WQS, із зображенням оновлення.
QueueHammer

1

Нижній приклад спрощений на основі коду № 4 Doin, спрощує цей код, використовуючи document.writeзамість src того, iframeщоб підтримувати CORS. Також фокусується лише на перевантаженні кеш-пам’ятника браузера, а не на перезавантаження кожного зображення на сторінку.

Нижче написано в typescriptі використовується бібліотека обіцянок angular $ q , просто fyi, але вона повинна бути досить простою для того, щоб перенести ванільний javascript. Метод призначений жити всередині класу машинопису.

Повертає обіцянку, яка буде вирішена, коли iframe завершить перезавантаження. Не сильно перевірений, але добре працює для нас.

    mmForceImgReload(src: string): ng.IPromise<void> {
        var deferred = $q.defer<void>();
        var iframe = window.document.createElement("iframe");

        var firstLoad = true;
        var loadCallback = (e) => {
            if (firstLoad) {
                firstLoad = false;
                iframe.contentWindow.location.reload(true);
            } else {
                if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
                deferred.resolve();
            }
        }
        iframe.style.display = "none";
        window.parent.document.body.appendChild(iframe);
        iframe.addEventListener("load", loadCallback, false);
        iframe.addEventListener("error", loadCallback, false);
        var doc = iframe.contentWindow.document;
        doc.open();
        doc.write('<html><head><title></title></head><body><img src="' + src + '"></body></html>');
        doc.close();
        return deferred.promise;
    }

Щоб захистити від помилок XSS, вам слід + encodeURI(src) +правильно втекти srcв iframe.
Тіно

1

Наступний код корисний для оновлення зображення при натисканні кнопки.

function reloadImage(imageId) {
   imgName = 'vishnu.jpg'; //for example
   imageObject = document.getElementById(imageId);
   imageObject.src = imgName;
}

<img src='vishnu.jpg' id='myimage' />

<input type='button' onclick="reloadImage('myimage')" />

Захищений. Оскільки це лише трохи змінена копія коду @ Mahmoud, але на відміну від цього НЕ освіжає зображення
Tino

0

Я вирішив цю проблему, відправивши дані назад через сервлет.

response.setContentType("image/png");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.setDateHeader("Expires", 0);

BufferedImage img = ImageIO.read(new File(imageFileName));

ImageIO.write(img, "png", response.getOutputStream());

Потім зі сторінки ви просто даєте сервлету з деякими парамами, щоб схопити правильний файл зображення.

<img src="YourServlet?imageFileName=imageNum1">

0

Ось моє рішення. Це дуже просто. Графік кадру може бути кращим.

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">      
        <title>Image Refresh</title>
    </head>

    <body>

    <!-- Get the initial image. -->
    <img id="frame" src="frame.jpg">

    <script>        
        // Use an off-screen image to load the next frame.
        var img = new Image();

        // When it is loaded...
        img.addEventListener("load", function() {

            // Set the on-screen image to the same source. This should be instant because
            // it is already loaded.
            document.getElementById("frame").src = img.src;

            // Schedule loading the next frame.
            setTimeout(function() {
                img.src = "frame.jpg?" + (new Date).getTime();
            }, 1000/15); // 15 FPS (more or less)
        })

        // Start the loading process.
        img.src = "frame.jpg?" + (new Date).getTime();
    </script>
    </body>
</html>

0

Немає потреби в new Date().getTime()шенганянах. Ви можете обдурити браузер, створивши невидимий фіктивний образ і скориставшись jQuery .load (), а потім створюючи нове зображення кожного разу:

<img src="" id="dummy", style="display:none;" />  <!-- dummy img -->
<div id="pic"></div>

<script type="text/javascript">
  var url = whatever;
  // You can repeat the following as often as you like with the same url
  $("#dummy").load(url);
  var image = new Image();
  image.src = url;
  $("#pic").html("").append(image);
</script>

0

Просте рішення: додайте цей заголовок до відповіді:

Cache-control: no-store

Чому це працює, чітко пояснено на цій авторитетній сторінці: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

Це також пояснює, чому no-cacheце не працює.

Інші відповіді не працюють, оскільки:

Caching.deleteпро новий кеш, який ви можете створити для роботи в режимі офлайн, див: https://web.dev/cache-api-quick-guide/

Фрагменти, що використовують # в URL-адресі, не працюють, оскільки # повідомляє браузеру не надсилати запит на сервер.

Кеш-бустер із випадковою частиною, доданою до URL-адреси, працює, але також заповнить кеш браузера. У своєму додатку я хотів завантажувати зображення з 5 Мб кожні кілька секунд з веб-камери. Щоб повністю заморозити ПК, знадобиться всього лише годину або менше. Я досі не знаю, чому кеш браузера не обмежується розумним максимумом, але це, безумовно, недолік.


0

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

  <h3>Webcam</h3>
  <p align="center">
    <img id="webcam" title="Webcam" onload="updateImage();" src="https://www.your-domain.com/webcam/current.jpg" alt="webcam image" width="900" border="0" />

    <script type="text/javascript" language="JavaScript">

    // off-screen image to preload next image
    var newImage = new Image();
    newImage.src = "https://www.your-domain.com/webcam/current.jpg";

    // remember the image height to prevent showing broken images
    var height = newImage.naturalHeight;

    function updateImage()
    {
        // for sure if the first image was a broken image
        if(newImage.naturalHeight > height)
        {
          height = newImage.naturalHeight;
        }

        // off-screen image loaded and the image was not broken
        if(newImage.complete && newImage.naturalHeight == height) 
        {
          // show the preloaded image on page
          document.getElementById("webcam").src = newImage.src;
        }

        // preload next image with cachebreaker
        newImage.src = "https://www.your-domain.com/webcam/current.jpg?time=" + new Date().getTime();

        // refresh image (set the refresh interval to half of webcam refresh, 
        // in my case the webcam refreshes every 5 seconds)
        setTimeout(updateImage, 2500);
    }

    </script>
</p>

-3

Я використав нижченаведене поняття: спочатку зв'язати зображення з помилковою (буферною) URL-адресою, а потім зв'язати його з дійсною URL-адресою

imgcover.ImageUrl = ConfigurationManager.AppSettings["profileLargeImgPath"] + "Myapp_CoverPic_" + userid + "Buffer.jpg";

imgcover.ImageUrl = ConfigurationManager.AppSettings["profileLargeImgPath"] + "Myapp_CoverPic_" + userid + ".jpg";

Таким чином, я змушую браузер оновити дійсну URL-адресу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.