Обновить изображение с новым на тот же 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

Мне нужно принудительно обновить только это изображение на странице. Любые идеи?

QueueHammer
источник
См stackoverflow.com/questions/126772/...
Филибер Perusse

Ответы:

351

Попробуйте добавить кеш-взломщик в конце URL:

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

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

Паоло Бергантино
источник
26
Не очень хорошее решение, так как оно затопит кеши (как локальные, так и вышестоящие). Ответ Аи имеет лучший способ справиться с этим.
Tgr
1
Кроме того, повторное отображение того же изображения в другом месте, без «прерывателя кэша» позже, все еще показывает старую кэшированную версию (по крайней мере, в Firefox) оба? и # :(
T4NK3R
3
Вы можете сделать еще меньше кода с этим:'image.jpg?' + (+new Date())
Лев Лукомский
4
Есть Date.now()для этого
vp_arth
2
Почему бы нетMath.random()
Гаутам Гопалакришнан
227

Я видел много вариантов ответов о том, как это сделать, поэтому я решил обобщить их здесь (плюс добавить 4-й метод моего собственного изобретения):


(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и добавьте в URL уникальный идентификатор фрагмента, разрушающего memcache , например:

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 [success, width, height], тогда ваш код может выглядеть примерно так:

// 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, в этих случаях.

Doin
источник
Мне нравится идея метода 4, но вы не можете загрузить внешний контент с помощью Iframe, не так ли? В настоящее время я использую метод 3 в одностраничном веб-приложении, но мне не нравится тот факт, что вам нужно перезагрузить всю страницу, чтобы получить новое изображение, даже если HTML-код шаблона перезагружается.
Emilios1995
@Emilios: ... Кроме того, я не понимаю ваш комментарий о необходимости перезагрузить всю страницу. Оба метода (3) и (4) могут быть реализованы в javascript на стороне клиента, без перезагрузки ничего, кроме одного изображения, которое вы обновляете. Для метода (3) это просто означало бы использование javascript для изменения свойства src вашего изображения с (например) image.jpg#123на image.jpg#124(или как угодно, до тех пор, пока бит после «#» не изменится). Не могли бы вы уточнить, что именно вы перезагружаете и почему?
Doin
@Emilios: вы действительно можете загрузить внешний (междоменный) контент в iframe ... но вы не сможете получить к нему доступ contentWindowчерез javascript для выполнения reload(true)вызова, который является критической частью метода ... так что нет, method (4) ) не будет работать для междоменного контента. Хорошо подмечено; Я обновлю «Минусы», чтобы включить это.
Doin
@ Emilios: Ой, нет, я не буду: я понял, что простое исправление (теперь включено в мой ответ) позволяет работать и для междоменных изображений (при условии, что вы можете разместить серверный скрипт на своем сервере).
Doin
@pseudosavant: К сожалению, я заметил это только ~ 17 месяцев спустя, но мне жаль, что я должен сказать вам, что ваши изменения в моем коде были сильно нарушены. (Честно говоря, я не думаю, что код обратного вызова, который у меня был изначально, тоже был прав). Я теперь переписал часть (4), как объяснение, так и код. Ваш предыдущий код никогда не очищал изображения (поэтому он мог ошибаться нечетными способами, особенно в IE, особенно если изображение показывалось в нескольких местах), но, что еще хуже, он также удалял iframe сразу после начала полной перезагрузки, что, вероятно, означало, что работать только с перерывами или не работать вообще. Сожалею!
Doin
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
Большой! После долгих попыток загрузить мои веб-изображения, которые загружались с задержкой, я только что решил ее, применив ваше решение (с «#», использование «?» Не работает для меня). Большое спасибо!!!
user304602
18
Здесь задействованы ДВА кеша: это обычный HTTP-кеш браузера и кэш изображений в памяти, которые он недавно отображал. Этот последний кэш в памяти индексируется полным srcатрибутом, поэтому добавление уникального идентификатора фрагмента гарантирует, что изображение не будет просто извлечено из памяти. Но идентификаторы фрагментов не отправляются как часть HTTP-запросов, поэтому обычный HTTP-кеш будет использоваться как обычно. Вот почему эта техника работает.
Doin
Есть несколько кеш заголовков. на самом деле я не очень хорошо знаю английский, не могли бы вы сказать мне, какой я должен использовать ?! Я хочу что-то, что не кеширует только измененную фотографию (например, капчу), и кеширует другие вещи. так Cache-Control: max-age=0, must-revalidateхорошо для меня?
Шафизаде
Это не работает для меня. Единственное, что отличается в моем случае, это то, что у меня есть URL к действию контроллера, которое извлекает img из БД. У меня были другие аргументы для действия контроллера, поэтому я добавил его как "...... & convert = true & t =" + new Date (). GetTime (); и "...... & convert = true #" + new Date (). 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);
}

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

BaroqueBobcat
источник
3

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

Лучший ответ - добавить пару дополнительных параметров в заголовок HTTP.

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

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

Эдвард КМЕТТ
источник
3
Это звучало так, как будто бы это сработало, но все равно показывает то же изображение, даже с взломами. Я добавлю информацию заголовка к вопросу.
QueueHammer
Я только что заметил, что вы обновляете один и тот же тег img снова и снова. Браузер, вероятно, обнаруживает, когда вы устанавливаете src, что src не изменился, и поэтому его не надо обновлять. (Поскольку эта проверка происходит на уровне DOM и не имеет ничего общего с целью). Что произойдет, если вы добавите "?" + номер - на URL извлекаемого изображения?
Эдвард КМЕТТ
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 ... Вы должны изменить это на надлежащие события загрузки.

jazzcat
источник
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 секунду. Я использую это на странице с домашними камерами безопасности различного типа.

J Fields
источник
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);
}
QueueHammer
источник
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
пожалуйста, объясните ОП, как и почему это помогает, вместо того, чтобы просто вставлять код
nomistic
Я не думаю, что ../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.

Hardik
источник
1

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

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);
}
Joel
источник
Добавил WQS к коду и проверил, что запрос принимается и что браузер видит ответ как поступающий с адреса + WQS без обновления изображения.
QueueHammer
1

Крупный основанный на коде Играет в # 4, в приведенном ниже примере , что упрощает код отличный бит , используя document.writeвместо того , чтобы src в iframeподдержки CORS. Также фокусируется только на очистке кеша браузера, а не на перезагрузке каждого изображения на странице.

Ниже написано typescriptи использует библиотеку обещаний angular $ q , просто fyi, но она должна быть достаточно простой для переноса на vanilla 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')" />
Codemaker
источник
Downvoted. Так как это всего лишь слегка измененная копия кода @ 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">
tinymothbrain
источник
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>
Timmmm
источник
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>
Bazley
источник
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 МБ каждые несколько секунд с веб-камеры. Это займет всего час или меньше, чтобы полностью заморозить ваш компьютер. Я до сих пор не знаю, почему кеш браузера не ограничен разумным максимумом, но это определенно недостаток.

Roland
источник
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>
std8590
источник
-3

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

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

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

Таким образом, я заставляю браузер обновляться с действительным URL.

Прашант Шивасубрамани
источник