Aktualisieren Sie das Bild mit einem neuen Bild unter derselben URL

333

Ich greife auf einen Link auf meiner Website zu, der bei jedem Zugriff ein neues Bild bereitstellt.

Das Problem, auf das ich stoße, ist, dass sich das Bild nicht ändert, wenn ich versuche, das Bild im Hintergrund zu laden und dann das auf der Seite zu aktualisieren - obwohl es aktualisiert wird, wenn ich die Seite neu lade.

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);
}

Header, wie FireFox sie sieht:

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

Ich muss eine Aktualisierung nur dieses Bildes auf der Seite erzwingen. Irgendwelche Ideen?

QueueHammer
quelle
Siehe stackoverflow.com/questions/126772/…
Philibert Perusse

Antworten:

351

Versuchen Sie, am Ende der URL einen Cachebreaker hinzuzufügen:

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

Dadurch wird der aktuelle Zeitstempel automatisch angehängt, wenn Sie das Bild erstellen, und der Browser sucht erneut nach dem Bild, anstatt das Bild im Cache abzurufen.

Paolo Bergantino
quelle
26
Keine sehr gute Lösung, da sie die Caches (sowohl lokal als auch stromaufwärts) überfluten wird. Ayas Antwort hat einen besseren Weg, damit umzugehen.
Tgr
1
Wenn Sie dasselbe Bild an anderer Stelle erneut anzeigen, ohne später den "Cache Breaker" zu verwenden, wird weiterhin die alte zwischengespeicherte Version (zumindest in Firefox) angezeigt. und # :(
T4NK3R
3
Sie können damit noch weniger Code erstellen:'image.jpg?' + (+new Date())
Lev Lukomsky
4
Es gibt Date.now()dafür
vp_arth
2
Warum nichtMath.random()
Gowtham Gopalakrishnan
227

Ich habe viele Variationen bei den Antworten dafür gesehen, daher dachte ich, ich würde sie hier zusammenfassen (plus eine vierte Methode meiner eigenen Erfindung hinzufügen):


(1) Fügen Sie der URL einen eindeutigen Cache-Busting-Abfrageparameter hinzu, z.

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

Vorteile: 100% zuverlässig, schnell und einfach zu verstehen und zu implementieren.

Nachteile: Umgeht das Caching insgesamt, was unnötige Verzögerungen und Bandbreitennutzung bedeutet, wenn sich das Bild zwischen den Ansichten nicht ändert. Füllt möglicherweise den Browser-Cache (und alle Zwischen-Caches) mit vielen, vielen Kopien genau desselben Bildes! Außerdem muss die Bild-URL geändert werden.

Verwendungszweck: Verwenden Sie diese Option, wenn sich das Bild ständig ändert, z. B. für einen Live-Webcam-Feed. Wenn Sie diese Methode verwenden, stellen Sie sicher, dass Sie die Bilder selbst mit Cache-control: no-cacheHTTP-Headern versorgen !!! (Oft kann dies mit einer .htaccess-Datei eingerichtet werden). Andernfalls füllen Sie die Caches nach und nach mit alten Versionen des Bildes!


(2) Fügen Sie der URL einen Abfrageparameter hinzu, der sich nur ändert, wenn die Datei dies tut, z.

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

(Das ist PHP-serverseitiger Code, aber der wichtige Punkt hier ist nur, dass ein? M = [ Dateilast der letzten Änderung] an den Dateinamen angehängt wird).

Vorteile : 100% zuverlässig, schnell und einfach zu verstehen und zu implementieren und die Caching-Vorteile perfekt zu erhalten.

Nachteile: Erfordert das Ändern der Bild-URL. Außerdem etwas mehr Arbeit für den Server - er muss Zugriff auf die zuletzt geänderte Zeit der Datei erhalten. Erfordert außerdem serverseitige Informationen und ist daher nicht für eine rein clientseitige Lösung geeignet, um nach einem aktualisierten Image zu suchen.

Verwendungszweck: Wenn Sie Bilder zwischenspeichern möchten, diese jedoch möglicherweise von Zeit zu Zeit am Serverende aktualisieren müssen, ohne den Dateinamen selbst zu ändern. UND wenn Sie leicht sicherstellen können, dass jeder Bildinstanz in Ihrem HTML-Code der richtige Querystring hinzugefügt wird.


(3) Servieren Sie Ihre Bilder mit dem Header Cache-control: max-age=0, must-revalidateund fügen Sie der URL eine eindeutige Memcache- Busting-Fragment-ID hinzu, z.

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

Die Idee dabei ist, dass der Cache-Control-Header Bilder in den Browser-Cache legt, sie jedoch sofort als veraltet markiert, sodass der Browser bei jeder erneuten Anzeige beim Server nachfragen muss, ob sie geändert wurden. Dadurch wird sichergestellt, dass der HTTP-Cache des Browsers immer die neueste Kopie des Bildes zurückgibt. Browser verwenden jedoch häufig eine speicherinterne Kopie eines Bildes wieder, wenn sie eine haben, und überprüfen in diesem Fall nicht einmal ihren HTTP-Cache. Um dies zu verhindern, wird eine Fragmentkennung verwendet: Der Vergleich von In-Memory src-Bildern enthält die Fragmentkennung, wird jedoch entfernt, bevor der HTTP-Cache abgefragt wird. (Also zB image.jpg#Aund image.jpg#Bmöglicherweise werden beide aus dem image.jpgEintrag im HTTP-Cache des Browsers angezeigt , aberimage.jpg#Bwürde niemals mit speicherinternen Bilddaten von dem image.jpg#AZeitpunkt angezeigt werden, an dem sie zuletzt angezeigt wurden).

Vorteile: Verwendet die HTTP-Caching-Mechanismen ordnungsgemäß und verwendet zwischengespeicherte Bilder, wenn sie sich nicht geändert haben. Funktioniert für Server, die an einem Querystring ersticken, der einer statischen Image-URL hinzugefügt wurde (da Server niemals Fragment-IDs sehen - sie sind nur für den eigenen Gebrauch durch den Browser bestimmt).

Nachteile: Stützt sich auf ein etwas zweifelhaftes (oder zumindest schlecht dokumentiertes) Verhalten von Browsern in Bezug auf Bilder mit Fragment-IDs in ihren URLs (ich habe dies jedoch erfolgreich in FF27, Chrome33 und IE11 getestet). Sendet weiterhin für jede Bildansicht eine Revalidierungsanforderung an den Server. Dies kann zu einem Overkill führen, wenn sich Bilder nur selten ändern und / oder die Latenz ein großes Problem darstellt (da Sie auf die Revalidierungsantwort warten müssen, auch wenn das zwischengespeicherte Image noch gut ist). . Erfordert das Ändern von Bild-URLs.

Verwendungszweck: Verwenden Sie diese Option, wenn sich Bilder häufig ändern oder vom Client zeitweise aktualisiert werden müssen, ohne dass ein serverseitiges Skript erforderlich ist, Sie jedoch den Vorteil des Caching wünschen. Zum Beispiel das Abrufen einer Live-Webcam, die alle paar Minuten ein Bild unregelmäßig aktualisiert. Verwenden Sie alternativ anstelle von (1) oder (2), wenn Ihr Server keine Abfragezeichenfolgen für statische Bild-URLs zulässt.


(4) Aktualisieren Sie ein bestimmtes Bild zwangsweise mit Javascript, indem Sie es zuerst in ein verstecktes Bild laden <iframe>und dann location.reload(true)den Iframe aufrufen contentWindow.

Die Schritte sind:

  • Laden Sie das zu aktualisierende Bild in einen versteckten Iframe. Dies ist nur ein Einrichtungsschritt - auf Wunsch kann dies lange vor der eigentlichen Aktualisierung erfolgen. Es spielt nicht einmal eine Rolle, ob das Bild zu diesem Zeitpunkt nicht geladen werden kann!

  • Wenn dies erledigt ist, löschen Sie alle Kopien dieses Bildes auf Ihren Seiten oder irgendwo in DOM-Knoten (auch außerhalb von Seiten, die in Javascript-Variablen gespeichert sind). Dies ist erforderlich, da der Browser das Bild möglicherweise andernfalls von einer veralteten In-Memory-Kopie anzeigt (insbesondere IE11): Sie müssen sicherstellen, dass alle In-Memory- Kopien gelöscht werden, bevor Sie den HTTP-Cache aktualisieren . Wenn anderer Javascript-Code asynchron ausgeführt wird, müssen Sie möglicherweise auch verhindern, dass dieser Code in der Zwischenzeit neue Kopien des zu aktualisierenden Bildes erstellt.

  • Rufen Sie an iframe.contentWindow.location.reload(true). Die trueKräfte ein Cache - Bypass, direkt vom Server neu zu laden und die bestehende cached Kopie überschreibt.

  • Stellen Sie nach dem erneuten Laden die ausgeblendeten Bilder wieder her. Sie sollten jetzt die neue Version vom Server anzeigen!

Bei Bildern derselben Domäne können Sie das Bild direkt in den Iframe laden. Bei domänenübergreifenden Bildern müssen Sie stattdessen eine HTML-Seite aus Ihrer Domäne laden , die das Bild in einem <img>Tag enthält. Andernfalls wird beim Aufruf eines Anrufs der Fehler "Zugriff verweigert" angezeigt iframe.contentWindow.reload(...).

Vorteile : Funktioniert genau wie die Funktion image.reload (), die das DOM gerne hätte! Ermöglicht das normale Zwischenspeichern von Bildern (auch mit zukünftigen Ablaufdaten, wenn Sie dies wünschen, wodurch häufige erneute Validierungen vermieden werden). Ermöglicht das Aktualisieren eines bestimmten Bildes, ohne die URLs für dieses Bild auf der aktuellen Seite oder auf anderen Seiten zu ändern, wobei nur clientseitiger Code verwendet wird.

Nachteile: Verlässt sich auf Javascript. Es wird nicht garantiert, dass 100% in jedem Browser ordnungsgemäß funktionieren (ich habe dies jedoch erfolgreich in FF27, Chrome33 und IE11 getestet). Sehr kompliziert im Vergleich zu den anderen Methoden.

Verwendungszweck: Wenn Sie eine Sammlung von grundsätzlich statischen Bildern haben, die Sie zwischengespeichert haben möchten, diese aber gelegentlich aktualisieren müssen und sofort visuelles Feedback erhalten, dass die Aktualisierung stattgefunden hat. (Insbesondere, wenn nur das Aktualisieren der gesamten Browserseite nicht funktioniert, wie beispielsweise bei einigen auf AJAX basierenden Webanwendungen). Und wenn die Methoden (1) - (3) nicht durchführbar sind, weil Sie (aus welchen Gründen auch immer) nicht alle URLs ändern können, die möglicherweise das Bild anzeigen, das Sie aktualisieren müssen. (Beachten Sie, dass mit diesen drei Methoden das Bild aktualisiert wird. Wenn jedoch eine andere Seite versucht, dieses Bild ohne den entsprechenden Querystring oder die entsprechende Fragmentkennung anzuzeigen, wird möglicherweise stattdessen eine ältere Version angezeigt.)

Die Details für eine faire und flexible Umsetzung sind nachstehend aufgeführt:

Nehmen wir an, Ihre Website enthält ein leeres GIF mit 1 x 1 Pixel im URL-Pfad /img/1x1blank.gifsowie das folgende einzeilige PHP-Skript (das nur zum Anwenden einer erzwungenen Aktualisierung auf domänenübergreifende Bilder erforderlich ist und in jeder serverseitigen Skriptsprache neu geschrieben werden kann natürlich) unter dem URL-Pfad /echoimg.php:

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

Im Folgenden finden Sie eine realistische Implementierung, wie Sie dies alles in Javascript tun können. Es sieht ein bisschen kompliziert aus, aber es gibt viele Kommentare, und die wichtige Funktion ist forceImgReload () - die ersten beiden nur leeren und nicht leeren Bilder. Sie sollten so gestaltet sein, dass sie effizient mit Ihrem eigenen HTML-Code arbeiten. Codieren Sie sie also als funktioniert am besten für Sie; Viele der damit verbundenen Komplikationen sind für Ihre Website möglicherweise nicht erforderlich:

// 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);
}

Um eine Aktualisierung eines Bildes zu erzwingen, das sich in derselben Domain wie Ihre Seite befindet, können Sie einfach Folgendes tun:

forceImgReload("myimage.jpg");

So aktualisieren Sie ein Bild von einem anderen Ort (domänenübergreifend):

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

Eine fortgeschrittenere Anwendung könnte darin bestehen, ein Bild nach dem Hochladen einer neuen Version auf Ihren Server neu zu laden und gleichzeitig mit dem Hochladen die Anfangsphase des Neuladevorgangs vorzubereiten, um die sichtbare Verzögerung beim erneuten Laden für den Benutzer zu minimieren. Wenn Sie den Upload über AJAX durchführen und der Server ein sehr einfaches JSON-Array [Erfolg, Breite, Höhe] zurückgibt, sieht Ihr Code möglicherweise folgendermaßen aus:

// 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));
}

Ein letzter Hinweis: Obwohl es sich bei diesem Thema um Bilder handelt, gilt es möglicherweise auch für andere Arten von Dateien oder Ressourcen. Verhindern Sie beispielsweise die Verwendung veralteter Skript- oder CSS-Dateien oder aktualisieren Sie möglicherweise sogar aktualisierte PDF-Dokumente (verwenden Sie (4) nur, wenn diese zum Öffnen im Browser eingerichtet sind). Methode (4) erfordert in diesen Fällen möglicherweise einige Änderungen am obigen Javascript.

Doin
quelle
Ich mag die Idee von Methode 4, aber Sie können keinen externen Inhalt mit einem Iframe laden, oder? Ich verwende derzeit Methode 3 in einer einseitigen Web-App, aber es gefällt mir nicht, dass Sie die gesamte Seite neu laden müssen, um das neue Bild zu erhalten, selbst wenn der HTML-Code der Vorlage neu geladen wird.
Emilios 1995
@Emilios: ... Außerdem verstehe ich Ihren Kommentar zum erneuten Laden der gesamten Seite nicht. Beide Methoden (3) und (4) können in clientseitigem Javascript implementiert werden, ohne dass etwas außer dem einen Bild, das Sie aktualisieren, neu geladen wird. Für Methode (3) würde dies nur bedeuten, Javascript zu verwenden, um die Eigenschaft 'src' Ihres Bildes von (z. B.) image.jpg#123in image.jpg#124(oder was auch immer ) zu ändern , solange sich das Bit nach dem '#' ändert). Können Sie klarstellen, was Sie neu laden und warum?
Doin
@Emilios: Sie können zwar externen (domänenübergreifenden) Inhalt in einen Iframe laden ... aber Sie können dann nicht contentWindowüber Javascript darauf zugreifen , um den reload(true)Aufruf auszuführen , der der kritische Teil der Methode ist ... also nein, Methode (4) ) funktioniert nicht für domänenübergreifende Inhalte. Gut erkannt; Ich werde die "Nachteile" aktualisieren, um das einzuschließen.
Doin
@Emilios: Ups, nein, werde ich nicht: Ich habe festgestellt, dass ein einfacher Fix (der jetzt in meiner Antwort enthalten ist) es auch ermöglicht, domänenübergreifende Images zu verwenden (vorausgesetzt, Sie können ein serverseitiges Skript auf Ihrem Server ablegen).
Doin
@pseudosavant: Leider bemerke ich das erst ~ 17 Monate später, aber es tut mir leid, dass ich Ihnen sagen muss, dass Ihre Änderungen an meinem Code stark beschädigt wurden. (Um fair zu sein, ich glaube auch nicht, dass der Rückrufcode, den ich ursprünglich hatte, richtig war). Ich habe jetzt Teil (4) ausführlich umgeschrieben, sowohl Erklärung als auch Code. Ihr vorheriger Code hat niemals Bilder ausgeblendet (so dass er auf seltsame Weise fehlschlagen kann, insbesondere im IE und insbesondere, wenn das Bild an mehreren Stellen angezeigt wurde), aber schlimmer noch, er hat den Iframe sofort nach dem Start des vollständigen Neuladens gelöscht, was wahrscheinlich bedeutete, dass dies der Fall war arbeiten Sie nur zeitweise oder gar nicht. Es tut uns leid!
Doin
186

Als Alternative zu ...

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

...Es scheint, dass...

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

... reicht aus, um den Browser-Cache zu täuschen, ohne Upstream-Caches zu umgehen, vorausgesetzt, Sie haben die richtigen Cache-ControlHeader zurückgegeben. Obwohl Sie verwenden können ...

Cache-Control: no-cache, must-revalidate

... Sie verlieren die Vorteile der If-Modified-Sinceoder If-None-MatchHeader, also so etwas wie ...

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

... sollte verhindern, dass der Browser das gesamte Bild erneut herunterlädt, wenn es sich nicht geändert hat. Getestet und funktioniert mit IE, Firefox und Chrome. Ärgerlicherweise schlägt es auf Safari fehl, es sei denn, Sie verwenden ...

Cache-Control: no-store

... obwohl dies immer noch vorzuziehen ist, um Upstream-Caches mit Hunderten identischer Images zu füllen, insbesondere wenn sie auf Ihrem eigenen Server ausgeführt werden. ;-);

Update (28.09.2014): Heutzutage scheint es, dass Cache-Control: no-storees auch für Chrome benötigt wird.

Aya
quelle
1
Großartig! Nachdem ich viel Zeit damit verbracht habe, meine Webbilder zu laden, die mit einer Verzögerung geladen wurden, habe ich sie gerade durch Anwenden Ihrer Lösung gelöst (mit '#' funktioniert die Verwendung von '?' Bei mir nicht). Danke vielmals!!!
user304602
18
Hierbei handelt es sich um ZWEI Caches: Es gibt den regulären HTTP-Cache des Browsers und einen speicherinternen Cache mit Bildern, die kürzlich angezeigt wurden. Dieser letztere speicherinterne Cache wird durch das vollständige srcAttribut indiziert. Durch Hinzufügen einer eindeutigen Fragmentkennung wird sichergestellt, dass das Bild nicht einfach aus dem Speicher abgerufen wird. Fragment-IDs werden jedoch nicht als Teil von HTTP-Anforderungen gesendet, sodass der reguläre HTTP-Cache wie gewohnt verwendet wird. Deshalb funktioniert diese Technik.
Doin
Es gibt mehrere Header-Cache. Eigentlich kann ich nicht sehr gut Englisch. Kannst du mir bitte sagen, ob ich welches verwenden soll?! Ich möchte etwas, das nicht nur das geänderte Foto (wie ein Captcha) zwischenspeichert und andere Dinge zwischenspeichert. so Cache-Control: max-age=0, must-revalidateist gut für mich?
Shafizadeh
Es funktioniert nicht bei mir. Das einzige, was in meinem Fall anders ist, ist, dass ich eine URL zu einer Controller-Aktion habe, die img von db abruft. Ich hatte andere Argumente für die Controller-Aktion und fügte sie als "...... & convert = true & t =" + new Date () hinzu. GetTime (); und "...... & convert = true #" + new Date (). getTime ();. Gibt es etwas, was ich falsch mache?
Shaffooo
1
Um den Aufwand für die Objekterstellung und / oder den Methodenaufruf zu vermeiden, können Sie eine inkrementierende Ganzzahl als Cache-Buster verwenden:newImage.src = "http://localhost/image.jpg#" + i++;
Laindir
7

Entfernen Sie nach dem Erstellen des neuen Bildes das alte Bild aus dem DOM und ersetzen Sie es durch das neue?

Möglicherweise greifen Sie bei jedem Aufruf von updateImage nach neuen Bildern, fügen diese jedoch nicht zur Seite hinzu.

Es gibt verschiedene Möglichkeiten, dies zu tun. So etwas würde funktionieren.

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);
}

Wenn das immer noch Probleme gibt, handelt es sich wahrscheinlich um ein Caching-Problem, über das in den anderen Antworten gesprochen wird.

BarockBobcat
quelle
3

Eine Antwort besteht darin, einige get-Abfrageparameter, wie vorgeschlagen, hackisch hinzuzufügen.

Eine bessere Antwort besteht darin, einige zusätzliche Optionen in Ihrem HTTP-Header auszugeben.

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

Wenn Sie ein Datum in der Vergangenheit angeben, wird es vom Browser nicht zwischengespeichert. Cache-Controlwurde in HTTP / 1.1 hinzugefügt und das Must-Revalidate-Tag zeigt an, dass Proxys auch unter mildernden Umständen niemals ein altes Image bereitstellen sollten. Dies Pragma: no-cacheist für aktuelle moderne Browser / Caches nicht unbedingt erforderlich, kann aber bei einigen kaputten, kaputten alten Implementierungen hilfreich sein.

Edward KMETT
quelle
3
Das klang, als hätte es funktioniert, aber es zeigt immer noch das gleiche Bild, auch mit den Hacks. Ich werde die Header-Informationen zur Frage hinzufügen.
QueueHammer
Mir ist gerade aufgefallen, dass Sie immer wieder dasselbe img-Tag aktualisieren. Der Browser erkennt wahrscheinlich, wenn Sie den src einstellen, dass sich der src nicht geändert hat, und macht sich daher nicht die Mühe, ihn zu aktualisieren. (Da diese Prüfung auf DOM-Ebene stattfindet und nichts mit dem Ziel zu tun hat). Was passiert, wenn Sie "?" + Nummer - zur URL des abgerufenen Bildes?
Edward KMETT
3

Ich hatte eine Anforderung: 1) kann ?var=xxdem Bild keine hinzufügen 2) es sollte domänenübergreifend funktionieren

Ich mag die Option Nr. 4 in dieser Antwort mit einer, aber:

  • Es gibt Probleme beim zuverlässigen Arbeiten mit Crossdomain (und es muss der Servercode berührt werden).

Mein schneller und schmutziger Weg ist:

  1. Erstelle einen versteckten Iframe
  2. Laden Sie die aktuelle Seite darauf (ja die ganze Seite)
  3. iframe.contentWindow.location.reload(true);
  4. Setzen Sie die Bildquelle auf sich selbst zurück

Hier ist es

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);
}

Ja, ich weiß, setTimeout ... Sie müssen das in richtige Onload-Ereignisse ändern.

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

Dann unten in etwas 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>

Wenn das Bild geladen wird, wird es in 1 Sekunde neu geladen. Ich verwende dies auf einer Seite mit Heimsicherheitskameras unterschiedlichen Typs.

J Felder
quelle
2

Am Ende ließ ich den Server jede Anforderung für ein Image in diesem Verzeichnis der Quelle zuordnen, die ich aktualisieren wollte. Ich ließ dann meinen Timer eine Nummer an das Ende des Namens anhängen, damit das DOM es als neues Bild sehen und es laden konnte.

Z.B

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

fordert den gleichen Bilderzeugungscode an, sieht jedoch für den Browser wie verschiedene Bilder aus.

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
quelle
8
Dies hätte das gleiche Problem beim Zwischenspeichern mehrerer Kopien des Bildes wie die Querystring-Lösung (Paolo und einige andere) und erfordert Serveränderungen.
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')" />

Mahmoud
quelle
3
Bitte erklären Sie dem OP, wie und warum dies hilft, anstatt nur Code
einzufügen
Ich glaube nicht, dass dies ../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit)ein gültiger Dateiname ist ... Zumindest versucht er dies zu laden ...
ByteHamster
Wechsel path='../showImage.php';zupath='../showImage.php?';
BOOMik
2
document.getElementById("img-id").src = document.getElementById("img-id").src

Setze seinen eigenen src als seinen src.

Hardik
quelle
1

Versuchen Sie es mit einem wertlosen Querystring, um daraus eine eindeutige URL zu machen:

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
quelle
WQS wurde zum Code hinzugefügt und überprüft, ob die Anforderung akzeptiert wird und ob der Browser die Antwort als von der Adresse + WQS stammend ansieht, ohne dass das Bild aktualisiert wird.
QueueHammer
1

Stark auf Doin der # 4 - Code, unter Verwendung des unter Beispiel vereinfacht , dass Code ein großes Stück document.writestatt src in der iframeCORS zu unterstützen. Konzentriert sich auch nur darauf, den Browser-Cache zu sprengen und nicht jedes Bild auf der Seite neu zu laden.

Das Folgende ist in geschrieben typescriptund verwendet die angular $ q- Versprechen-Bibliothek, nur zu Ihrer Information, sollte aber einfach genug sein, um auf Vanille-Javascript zu portieren. Die Methode soll in einer Typoskriptklasse leben.

Gibt ein Versprechen zurück, das aufgelöst wird, wenn der Iframe das Neuladen abgeschlossen hat. Nicht stark getestet, funktioniert aber gut für uns.

    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;
    }
Sieger
quelle
Zum Schutz vor XSS-Fehlern sollten Sie verwenden, + encodeURI(src) +um ordnungsgemäß srcin der zu entkommen iframe.
Tino
1

Der folgende Code ist nützlich, um das Bild zu aktualisieren, wenn auf eine Schaltfläche geklickt wird.

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
quelle
Abgestimmt. Da es sich nur um eine leicht modifizierte Kopie von @ Mahmouds Code handelt, wird das Bild hier jedoch NICHT aktualisiert
Tino
0

Ich habe dieses Problem gelöst, indem ich die Daten über ein Servlet zurückgesendet habe.

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());

Dann geben Sie auf der Seite einfach das Servlet mit einigen Parametern ein, um die richtige Bilddatei zu erhalten.

<img src="YourServlet?imageFileName=imageNum1">
Tinymothbrain
quelle
0

Hier ist meine Lösung. Es ist sehr einfach. Die Frame-Planung könnte besser sein.

<!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
quelle
0

Keine Notwendigkeit für new Date().getTime()Spielereien. Sie können den Browser austricksen, indem Sie ein unsichtbares Dummy-Bild haben und jQuery .load () verwenden und dann jedes Mal ein neues Bild erstellen:

<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
quelle
0

Einfache Lösung: Fügen Sie diesen Header zur Antwort hinzu:

Cache-control: no-store

Warum dies funktioniert, wird auf dieser maßgeblichen Seite klar erläutert: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

Es erklärt auch, warum no-cachenicht funktioniert.

Andere Antworten funktionieren nicht, weil:

Caching.deletehandelt von einem neuen Cache, den Sie für die Offline-Arbeit erstellen können, siehe: https://web.dev/cache-api-quick-guide/

Fragmente, die ein # in der URL verwenden, funktionieren nicht, da das # den Browser anweist, keine Anforderung an den Server zu senden.

Ein Cache-Buster mit einem zufälligen Teil, der der URL hinzugefügt wird, funktioniert, füllt aber auch den Browser-Cache. In meiner App wollte ich alle paar Sekunden ein 5-MB-Bild von einer Webcam herunterladen. Es dauert nur eine Stunde oder weniger, um Ihren PC vollständig einzufrieren. Ich weiß immer noch nicht, warum der Browser-Cache nicht auf ein vernünftiges Maximum beschränkt ist, aber das ist definitiv ein Nachteil.

Roland
quelle
0

Ich habe das Skript von AlexMA verbessert , um meine Webcam auf einer Webseite anzuzeigen , die regelmäßig ein neues Bild mit demselben Namen hochlädt. Ich hatte Probleme, dass das Bild manchmal aufgrund eines fehlerhaften Bildes oder eines nicht vollständig (hoch) geladenen Bildes flackerte. Um ein Flackern zu vermeiden, überprüfe ich die natürliche Höhe des Bildes, da sich die Größe meines Webcam-Bildes nicht geändert hat. Nur wenn die geladene Bildhöhe der ursprünglichen Bildhöhe entspricht, wird das vollständige Bild auf der Seite angezeigt.

  <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
quelle
-3

Ich habe das folgende Konzept verwendet, das Bild zuerst mit einer falschen (Puffer-) URL und dann mit der gültigen URL zu binden.

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

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

Auf diese Weise zwinge ich den Browser, mit einer gültigen URL zu aktualisieren.

Prashanth Shivasubramani
quelle