Aggiorna l'immagine con una nuova nello stesso URL


333

Sto accedendo a un link sul mio sito che fornirà una nuova immagine ogni volta che si accede.

Il problema in cui mi imbatto è che se provo a caricare l'immagine in background e quindi a aggiornare quella sulla pagina, l'immagine non cambia, anche se viene aggiornata quando ricarico la pagina.

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

Intestazioni come le vedono 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

Ho bisogno di forzare un aggiornamento di solo quell'immagine sulla pagina. Qualche idea?


Risposte:


351

Prova ad aggiungere un cachebreaker alla fine dell'URL:

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

Questo aggiungerà automaticamente il timestamp corrente durante la creazione dell'immagine e farà in modo che il browser ricerchi l'immagine invece di recuperare quella nella cache.


26
Non è una soluzione molto buona in quanto inonderà le cache (sia locali che a monte). La risposta di Aya ha un modo migliore di affrontarlo.
Tgr

1
Inoltre, ri-visualizzare la stessa immagine altrove, senza il "cache breaker" in seguito, mostra ancora la vecchia versione memorizzata nella cache (almeno in Firefox)? e # :(
T4NK3R,

3
Puoi fare ancora meno codice con questo:'image.jpg?' + (+new Date())
Lev Lukomsky,

4
C'è Date.now()per questo
vp_arth il

2
Perché noMath.random()
Gowtham Gopalakrishnan,

227

Ho visto molte variazioni nelle risposte su come farlo, quindi ho pensato di riassumerle qui (più aggiungere un quarto metodo della mia invenzione):


(1) Aggiungi un parametro di query di busting della cache univoco all'URL, come ad esempio:

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

Pro: affidabile al 100%, veloce e facile da capire e implementare.

Contro: ignora la memorizzazione nella cache del tutto, il che significa ritardi inutili e utilizzo della larghezza di banda ogni volta che l'immagine non cambia tra le visualizzazioni. Potrà riempire potenzialmente la cache del browser (e qualsiasi cache intermedia) con molte, molte copie esattamente della stessa immagine! Inoltre, richiede la modifica dell'URL dell'immagine.

Quando utilizzarlo: utilizzare quando l'immagine è in costante cambiamento, ad esempio per un feed live della webcam. Se usi questo metodo, assicurati di servire le immagini stesse con Cache-control: no-cachele intestazioni HTTP !!! (Spesso questo può essere impostato usando un file .htaccess). Altrimenti riempirai progressivamente le cache di vecchie versioni dell'immagine!


(2) Aggiungi il parametro di query all'URL che cambia solo quando il file lo fa, ad esempio:

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

(Questo è il codice lato server PHP, ma il punto importante qui è solo che una stringa di query ? M = [ora ultima modifica file] viene aggiunta al nome file).

Pro: affidabile al 100%, veloce e facile da capire e implementare e conserva perfettamente i vantaggi della memorizzazione nella cache.

Contro: richiede la modifica dell'URL dell'immagine. Inoltre, un po 'più di lavoro per il server: deve avere accesso all'ora dell'ultima modifica del file. Inoltre, richiede informazioni sul lato server, quindi non adatto a una soluzione puramente solo lato client per verificare un'immagine aggiornata.

Quando utilizzarlo: quando si desidera memorizzare nella cache le immagini, ma potrebbe essere necessario aggiornarle alla fine del server di volta in volta senza modificare il nome file stesso. E quando puoi facilmente assicurarti che la querystring corretta sia aggiunta a ogni istanza di immagine nel tuo HTML.


(3) Servi le tue immagini con l'intestazione Cache-control: max-age=0, must-revalidatee aggiungi un identificatore di frammento busto memcache univoco all'URL, come ad esempio:

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

L'idea qui è che l'intestazione di controllo della cache metta le immagini nella cache del browser, ma le contrassegna immediatamente come vecchie, quindi ogni volta che vengono nuovamente visualizzate il browser deve verificare con il server per vedere se sono state modificate. Ciò garantisce che la cache HTTP del browser restituisca sempre l'ultima copia dell'immagine. Tuttavia, i browser riutilizzeranno spesso una copia in memoria di un'immagine se ne hanno una, e in questo caso non controlleranno nemmeno la loro cache HTTP. Per evitare ciò, viene utilizzato un identificatore di frammento: il confronto delle immagini in memoria srcinclude l'identificatore di frammento, ma viene rimosso prima di interrogare la cache HTTP. (Ad esempio, image.jpg#Ae image.jpg#Bpotrebbero entrambi essere visualizzati dalla image.jpgvoce nella cache HTTP del browser, maimage.jpg#Bnon verrebbero mai visualizzati utilizzando i dati di immagine conservati in memoria image.jpg#Adall'ultima visualizzazione).

Pro: fa un uso corretto dei meccanismi di memorizzazione nella cache HTTP e utilizza immagini memorizzate nella cache se non sono state modificate. Funziona con server che soffocano su una stringa di query aggiunta a un URL di immagine statica (poiché i server non vedono mai identificatori di frammenti - sono solo per uso proprio dei browser).

Contro: si basa su un comportamento alquanto dubbio (o almeno scarsamente documentato) dei browser, per quanto riguarda le immagini con identificatori di frammenti nei loro URL (Tuttavia, l'ho testato con successo in FF27, Chrome33 e IE11). Invia comunque una richiesta di riconvalida al server per ogni visualizzazione dell'immagine, che può essere eccessiva se le immagini cambiano solo raramente e / o la latenza è un grosso problema (poiché è necessario attendere la risposta di riconvalida anche quando l'immagine memorizzata nella cache è ancora valida) . Richiede la modifica degli URL delle immagini.

Quando utilizzarlo: utilizzare quando le immagini possono cambiare frequentemente o devono essere aggiornate in modo intermittente dal client senza il coinvolgimento degli script sul lato server, ma dove si desidera ancora il vantaggio della memorizzazione nella cache. Ad esempio, il polling di una webcam live che aggiorna un'immagine in modo irregolare ogni pochi minuti. In alternativa, utilizzare invece di (1) o (2) se il server non consente stringhe di query su URL di immagini statiche.


(4) Aggiorna forzatamente una particolare immagine usando Javascript, caricandola prima in un nascosto <iframe>e quindi chiamando location.reload(true)l'iframe contentWindow.

I passaggi sono:

  • Carica l'immagine da aggiornare in un iframe nascosto. Questo è solo un passaggio di impostazione: se lo si desidera, è possibile eseguire con largo anticipo l'aggiornamento effettivo. Non importa nemmeno se l'immagine non si carica in questa fase!

  • Una volta fatto, svuota tutte le copie di quell'immagine sulla tua pagina (e) o ovunque in qualsiasi nodo DOM (anche quelli fuori pagina memorizzati in variabili javascript). Ciò è necessario perché altrimenti il ​​browser potrebbe visualizzare l'immagine da una copia in memoria non aggiornata (IE11 in particolare lo fa): è necessario assicurarsi che tutte le copie in memoria siano cancellate, prima di aggiornare la cache HTTP. Se un altro codice javascript viene eseguito in modo asincrono, nel frattempo potrebbe essere necessario impedire a quel codice di creare nuove copie dell'immagine da aggiornare.

  • Chiama iframe.contentWindow.location.reload(true). Le trueforze di una cache di bypass, ricaricare direttamente dal server e sovrascrivendo la copia memorizzata nella cache esistente.

  • Al termine del nuovo caricamento, ripristinare le immagini oscurate. Ora dovrebbero visualizzare la nuova versione dal server!

Per le immagini dello stesso dominio, è possibile caricare direttamente l'immagine nell'iframe. Per le immagini tra domini, devi invece caricare una pagina HTML dal tuo dominio che contenga l'immagine in un <img>tag, altrimenti otterrai un errore "Accesso negato" quando provi a chiamare iframe.contentWindow.reload(...).

Pro: Funziona proprio come la funzione image.reload () che desideri abbia avuto il DOM! Permette alle immagini di essere memorizzate nella cache normalmente (anche con date di scadenza future se le desideri, evitando così frequenti riconvalide). Consente di aggiornare una particolare immagine senza alterare gli URL per quella immagine nella pagina corrente o su qualsiasi altra pagina, utilizzando solo il codice lato client.

Contro: si basa su Javascript. Non garantito al 100% per funzionare correttamente in tutti i browser (l'ho testato con successo in FF27, Chrome33 e IE11). Molto complicato rispetto agli altri metodi.

Quando utilizzarlo: quando si dispone di una raccolta di immagini sostanzialmente statiche che si desidera memorizzare nella cache, ma è comunque necessario essere in grado di aggiornarle di tanto in tanto e ottenere un riscontro visivo immediato dell'avvenuto aggiornamento. (Soprattutto quando il solo aggiornamento dell'intera pagina del browser non funzionava, come ad esempio in alcune app Web basate su AJAX). E quando i metodi (1) - (3) non sono fattibili perché (per qualsiasi motivo) non puoi cambiare tutti gli URL che potrebbero potenzialmente visualizzare l'immagine che devi aggiornare. (Si noti che utilizzando questi 3 metodi l'immagine verrà aggiornata, ma se un'altra pagina tenta quindi di visualizzare quell'immagine senza l'identificatore di querystring o frammento appropriato, potrebbe invece mostrare una versione precedente).

Di seguito sono riportati i dettagli relativi all'attuazione in modo fiabesco e flessibile:

Supponiamo che il tuo sito Web contenga un .gif pixel 1x1 vuoto nel percorso dell'URL /img/1x1blank.gife abbia anche il seguente script PHP a una riga (richiesto solo per applicare l'aggiornamento forzato alle immagini tra domini e può essere riscritto in qualsiasi linguaggio di scripting lato server , ovviamente) nel percorso URL /echoimg.php:

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

Quindi, ecco un'implementazione realistica di come potresti fare tutto questo in Javascript. Sembra un po 'complicato, ma ci sono molti commenti e l'importante funzione è solo forceImgReload () - le prime due immagini vuote e non vuote, e dovrebbero essere progettate per funzionare in modo efficiente con il tuo HTML, quindi codificale come funziona meglio per te; molte delle complicazioni in esse contenute potrebbero non essere necessarie per il tuo sito Web:

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

Quindi, per forzare un aggiornamento di un'immagine situata nello stesso dominio della tua pagina, puoi semplicemente fare:

forceImgReload("myimage.jpg");

Per aggiornare un'immagine da qualche altra parte (interdominio):

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

Un'applicazione più avanzata potrebbe essere quella di ricaricare un'immagine dopo aver caricato una nuova versione sul server, preparando la fase iniziale del processo di ricarica simultaneamente al caricamento, per ridurre al minimo il ritardo di ricarica visibile per l'utente. Se stai eseguendo il caricamento tramite AJAX e il server restituisce un array JSON molto semplice [esito positivo, larghezza, altezza], il tuo codice potrebbe essere simile al seguente:

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

Un'ultima nota: sebbene questo argomento riguardi le immagini, si applica anche ad altri tipi di file o risorse. Ad esempio, impedire l'uso di script obsoleti o file CSS o forse anche l'aggiornamento di documenti PDF aggiornati (utilizzando (4) solo se impostato per aprire nel browser). Il metodo (4) potrebbe richiedere alcune modifiche al javascript sopra, in questi casi.


Mi piace l'idea del metodo 4, ma non puoi caricare contenuti esterni con un Iframe, vero? Attualmente sto usando il metodo 3 in un'app Web a pagina singola, ma non mi piace il fatto che devi ricaricare l'intera pagina per ottenere la nuova immagine, anche se l'HTML del modello viene ricaricato.
Emilios

@Emilios: ... Inoltre, non capisco il tuo commento sul dover ricaricare l'intera pagina. Entrambi i metodi (3) e (4) possono essere implementati in javascript sul lato client, senza ricaricare nulla tranne l'immagine che si sta aggiornando. Per il metodo (3) ciò significherebbe semplicemente usare JavaScript per cambiare la proprietà 'src' della tua immagine da (ad esempio) image.jpg#123a image.jpg#124(o qualsiasi altra cosa, purché il bit dopo il '#' cambi). Potresti chiarire che cosa stai ricaricando e perché?
Doin, il

@Emilios: puoi davvero caricare contenuto esterno (interdominio) in un iframe ... ma non puoi quindi accedervi contentWindowtramite javascript per fare la reload(true)chiamata che è la parte critica del metodo ... quindi no, metodo (4 ) non funzionerà per i contenuti tra domini. Ben individuato; Aggiornerò i "Contro" per includerlo.
Doin, il

@Emilios: Oops, no non lo farò: mi sono reso conto che una semplice correzione (ora inclusa nella mia risposta) gli consente di funzionare anche per le immagini tra domini (a condizione che tu possa mettere uno script lato server sul tuo server).
Doin,

@pseudosavant: Sfortunatamente lo sto notando solo ~ 17 mesi dopo, ma mi dispiace dirtelo, le tue modifiche al mio codice sono state gravemente interrotte. (Per essere onesti, non penso che il codice di callback che avevo inizialmente fosse giusto). Ora ho riscritto ampiamente la parte (4), sia spiegazione che codice. Il tuo codice precedente non ha mai cancellato le immagini (quindi potrebbe fallire in modi strani, specialmente in IE e specialmente se l'immagine fosse mostrata in più punti), ma peggio ha anche cancellato l'iframe immediatamente dopo aver avviato il ricaricamento completo, il che probabilmente significava che avrebbe lavorare solo in modo intermittente o per niente. Scusa!
Doin,

185

In alternativa a...

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

...sembra che...

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

... è sufficiente per ingannare la cache del browser senza bypassare le cache a monte, supponendo che tu abbia restituito le Cache-Controlintestazioni corrette . Sebbene tu possa usare ...

Cache-Control: no-cache, must-revalidate

... perdi i benefici delle intestazioni If-Modified-Sinceo If-None-Match, quindi qualcosa come ...

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

... dovrebbe impedire al browser di scaricare nuovamente l'intera immagine se non è stata effettivamente modificata. Testato e funzionante su IE, Firefox e Chrome. Disgraziatamente fallisce su Safari a meno che tu non usi ...

Cache-Control: no-store

... anche se questo potrebbe essere preferibile riempire le cache a monte con centinaia di immagini identiche, in particolare quando sono in esecuzione sul tuo server. ;-)

Aggiornamento (2014-09-28): al giorno d'oggi sembra che Cache-Control: no-storesia necessario anche per Chrome.


1
Grande! Dopo molto tempo cercando di caricare le mie immagini web che venivano caricate in ritardo, l'ho appena risolto applicando la soluzione (con '#', usando '?' Non funziona per me). Grazie molto!!!
user304602

18
Ci sono DUE cache coinvolte qui: c'è la normale cache HTTP del browser e una cache di immagini in memoria che ha visualizzato di recente. Quest'ultima cache in memoria è indicizzata dall'attributo completo src, quindi l'aggiunta di un identificatore di frammento univoco garantisce che l'immagine non venga semplicemente estratta dalla memoria. Ma gli identificatori di frammenti non vengono inviati come parte delle richieste HTTP, quindi la normale cache HTTP verrà utilizzata normalmente. Ecco perché questa tecnica funziona.
Doin,

Esistono diverse cache di intestazione. in realtà non conosco molto bene l'inglese, per favore, puoi dirmi se dovrei usare quale ?! Voglio qualcosa che non memorizzi nella cache solo la foto che viene modificata (come un captcha) e memorizza nella cache altre cose. quindi Cache-Control: max-age=0, must-revalidateva bene per me?
Shafizadeh,

Non funziona per me. L'unica cosa diversa nel mio caso è che ho un URL per un'azione del controller che recupera img da db. Ho avuto altri argomenti per l'azione del controller, quindi l'ho aggiunto come "...... & convert = true & t =" + new Date (). GetTime (); e "...... & convert = true #" + new Date (). getTime () ;. C'è qualcosa che sto facendo di sbagliato?
Shaffooo,

1
Per evitare il sovraccarico della creazione dell'oggetto e / o della chiamata al metodo, è possibile utilizzare un numero intero incrementale come cache-buster:newImage.src = "http://localhost/image.jpg#" + i++;
laindir

7

Dopo aver creato la nuova immagine, stai rimuovendo la vecchia immagine dal DOM e sostituendola con quella nuova?

Potresti prendere nuove immagini ad ogni aggiornamento dell'immagine, ma non aggiungerle alla pagina.

Esistono diversi modi per farlo. Qualcosa del genere avrebbe funzionato.

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

Dopo aver funzionato, se ci sono ancora problemi è probabilmente un problema di memorizzazione nella cache di cui parlano le altre risposte.


3

Una risposta è quella di aggiungere hackishly alcuni parametri get query come è stato suggerito.

Una risposta migliore è emettere un paio di opzioni extra nell'intestazione HTTP.

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

Fornendo una data nel passato, non verrà memorizzata nella cache dal browser. Cache-Controlè stato aggiunto in HTTP / 1.1 e il tag must-revalidate indica che i proxy non devono mai pubblicare una vecchia immagine anche in circostanze attenuanti, e ciò Pragma: no-cachenon è realmente necessario per gli attuali browser / cache moderni, ma può aiutare con alcune vecchie implementazioni non funzionanti.


3
Sembrava che avrebbe funzionato, ma mostra ancora la stessa immagine, anche con gli hack. Aggiungerò le informazioni dell'intestazione alla domanda.
QueueHammer,

Ho appena notato che stai aggiornando ripetutamente lo stesso tag img. Il browser sta probabilmente rilevando quando vai a impostare src che src non è cambiato, e quindi non si preoccupa di aggiornare. (Dal momento che questo controllo sta avvenendo a livello di DOM e non ha nulla a che fare con l'obiettivo). Cosa succede se aggiungi "?" + numero - all'URL dell'immagine che viene recuperata?
Edward KMETT,

3

Avevo un requisito: 1) non posso aggiungerne uno ?var=xxall'immagine 2) dovrebbe funzionare su più domini

Mi piace molto l'opzione 4 in questa risposta con una ma:

  • ha problemi a lavorare con il crossdomain in modo affidabile (e richiede di toccare il codice del server).

Il mio modo rapido e sporco è:

  1. Crea iframe nascosto
  2. Carica la pagina corrente su di essa (sì l'intera pagina)
  3. iframe.contentWindow.location.reload(true);
  4. Reimpostare la sorgente dell'immagine su se stessa

Ecco qui

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

Sì, lo so, setTimeout ... Devi cambiarlo in eventi onload corretti.


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

Quindi di seguito in alcuni 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>

E quindi ciò che fa è, quando l'immagine viene caricata, pianifica che venga ricaricata in 1 secondo. Lo sto usando su una pagina con telecamere di sicurezza domestiche di vario tipo.


2

Quello che ho finito per fare è stato fare in modo che il server mappasse qualsiasi richiesta per un'immagine in quella directory alla fonte che stavo cercando di aggiornare. Quindi il mio timer ha aggiunto un numero alla fine del nome in modo che il DOM lo vedesse come una nuova immagine e lo caricasse.

Per esempio

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

richiederà lo stesso codice di generazione delle immagini ma sembrerà come immagini diverse al browser.

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

8
Ciò avrebbe lo stesso problema di memorizzazione nella cache di più copie dell'immagine della soluzione di querystring (Paolo e alcuni altri) e richiedere modifiche al server.
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
per favore spiega al PO come e perché questo aiuta invece di incollare semplicemente il codice
nomistico

Non penso che ../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit)sia un nome file valido ... Almeno questo è ciò che tenta di caricare ...
ByteHamster

cambia path='../showImage.php';inpath='../showImage.php?';
BOOMik il

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

imposta il proprio src come proprio src.


1

Prova a utilizzare una stringa di query senza valore per renderla un URL unico:

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

Aggiunto il codice WQS al codice e verificato che la richiesta è stata accettata e che il browser vede la risposta come proveniente dall'indirizzo + WQS senza l'aggiornamento dell'immagine.
QueueHammer,

1

Fortemente basato sul codice n. 4 di Doin, l'esempio seguente semplifica notevolmente l'utilizzo document.writedel codice anziché src nel iframesupporto di CORS. Si concentra anche solo sul rompersi della cache del browser, non ricaricare tutte le immagini sulla pagina.

Di seguito è scritto typescripte utilizza la libreria promessa angular $ q , solo fyi, ma dovrebbe essere abbastanza facile da trasferire su javascript vanilla. Il metodo è pensato per vivere all'interno di una classe dattiloscritta.

Restituisce una promessa che verrà risolta quando l'iframe avrà completato il ricaricamento. Non pesantemente testato, ma funziona bene per noi.

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

Per proteggerti dai bug XSS devi usare + encodeURI(src) +per scappare correttamente srcin iframe.
Tino,

1

Il seguente codice è utile per aggiornare l'immagine quando si fa clic su un pulsante.

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')" />

Downvoted. Dato che è solo una copia leggermente modificata del codice di @ Mahmoud, ma al contrario questo qui NON sta aggiornando l'immagine
Tino

0

Ho risolto questo problema rinviando i dati tramite un servlet.

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

Quindi dalla pagina gli dai il servlet con alcuni parametri per afferrare il file di immagine corretto.

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

0

Ecco la mia soluzione È molto semplice. La pianificazione dei frame potrebbe essere migliore.

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

    <body>

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

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

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

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

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

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

0

Non c'è bisogno di new Date().getTime()shenanigans. Puoi ingannare il browser avendo un'immagine fittizia invisibile e usando jQuery .load (), quindi creando una nuova immagine ogni volta:

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

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

0

Soluzione semplice: aggiungi questa intestazione alla risposta:

Cache-control: no-store

Perché questo funziona è spiegato chiaramente in questa autorevole pagina: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

Spiega anche perché no-cachenon funziona.

Altre risposte non funzionano perché:

Caching.deleteriguarda una nuova cache che è possibile creare per il lavoro offline, vedere: https://web.dev/cache-api-quick-guide/

I frammenti che utilizzano un # nell'URL non funzionano perché # indica al browser di non inviare una richiesta al server.

Un cache-buster con una parte casuale aggiunta all'URL funziona, ma riempirà anche la cache del browser. Nella mia app, volevo scaricare un'immagine da 5 MB ogni pochi secondi da una web cam. Ci vorrà solo un'ora o meno per bloccare completamente il tuo PC. Non so ancora perché la cache del browser non sia limitata a un massimo ragionevole, ma questo è sicuramente uno svantaggio.


0

Ho migliorato la sceneggiatura di AlexMA per mostrare la mia webcam su una pagina Web che carica periodicamente una nuova immagine con lo stesso nome. Ho avuto problemi che a volte l'immagine tremolava a causa di un'immagine rotta o non completa (caricata). Per evitare lo sfarfallio, controllo l'altezza naturale dell'immagine perché la dimensione dell'immagine della mia webcam non è cambiata. Solo se l'altezza dell'immagine caricata corrisponde all'altezza dell'immagine originale, l'immagine completa verrà mostrata sulla pagina.

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

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

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

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

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

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

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

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

    </script>
</p>

-3

Ho usato il seguente concetto di prima associazione dell'immagine con un falso (buffer) url e poi associazione con l'URL valido.

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

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

In questo modo, sto forzando l'aggiornamento del browser con URL valido.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.