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-cache
HTTP-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-revalidate
und 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#A
und image.jpg#B
möglicherweise werden beide aus dem image.jpg
Eintrag im HTTP-Cache des Browsers angezeigt , aberimage.jpg#B
würde niemals mit speicherinternen Bilddaten von dem image.jpg#A
Zeitpunkt 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 true
Krä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.gif
sowie 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.