Come forzare un browser web a NON memorizzare nella cache le immagini


124

sfondo

Sto scrivendo e utilizzando uno strumento di gestione dei contenuti basato su CGI (Perl) molto semplice per due siti Web pro-bono. Fornisce all'amministratore del sito web moduli HTML per eventi in cui riempiono i campi (data, luogo, titolo, descrizione, collegamenti, ecc.) E li salvano. In quel form consento all'amministratore di caricare un'immagine relativa all'evento. Nella pagina HTML che visualizza il modulo, sto anche mostrando un'anteprima dell'immagine caricata (tag HTML img).

Il problema

Il problema si verifica quando l'amministratore vuole cambiare l'immagine. Dovrebbe solo premere il pulsante "sfoglia", scegliere una nuova immagine e premere ok. E questo funziona bene.

Una volta caricata l'immagine, il mio CGI back-end gestisce il caricamento e ricarica il modulo correttamente.

Il problema è che l'immagine mostrata non lo fa viene aggiornata. La vecchia immagine è ancora visualizzata, anche se il database contiene l'immagine giusta. L'ho ristretto al fatto che l'IMMAGINE È CACCIATA nel browser web. Se l'amministratore preme il pulsante RELOAD in Firefox / Explorer / Safari, tutto viene aggiornato correttamente e viene visualizzata la nuova immagine.

La mia soluzione - Non funziona

Sto cercando di controllare la cache scrivendo un'istruzione HTTP Expires con una data molto lontana nel passato.

Expires: Mon, 15 Sep 2003 1:00:00 GMT

Ricorda che sono dal lato amministrativo e non mi interessa davvero se le pagine impiegano un po 'più di tempo a caricarsi perché sono sempre scadute.

Ma anche questo non funziona.

Appunti

Quando si carica un'immagine, il suo nome file non viene mantenuto nel database. Viene rinominato come Image.jpg (per cose quando lo si utilizza). Quando si sostituisce l'immagine esistente con una nuova, neanche il nome cambia. Cambia solo il contenuto del file immagine.

Il server web è fornito dal servizio di hosting / ISP. Usa Apache.

Domanda

C'è un modo per forzare il browser web a NON memorizzare nella cache le cose di questa pagina, nemmeno le immagini?

Sto manipolando con l'opzione per effettivamente "salvare il nome del file" con il database. In questo modo, se l'immagine viene modificata, cambierà anche l'src del tag IMG. Tuttavia, questo richiede molte modifiche in tutto il sito e preferisco non farlo se ho una soluzione migliore. Inoltre, questo non funzionerà ancora se la nuova immagine caricata ha lo stesso nome (supponiamo che l'immagine sia stata ritoccata un po 'e ricaricata).


Penso che questa domanda possa essere rielaborata per rimuovere tutto il "contesto ormai inutile" che ho inserito tempo fa quando l'ho scritta. Penso che il titolo sia quello giusto e anche la risposta migliore. Ma la domanda è stata scritta originariamente per fornire molto spazio a molti tipi di risposte e non ci si poteva aspettare una soluzione così semplice. Pertanto la domanda è un po 'complicata per le persone che vengono lì per ottenere la risposta.
Philibert Perusse

Risposte:


186

Armin Ronacher ha l'idea giusta. Il problema è che stringhe casuali possono entrare in collisione. Io userei:

<img src="picture.jpg?1222259157.415" alt="">

Dove "1222259157.415" è l'ora corrente sul server.
Genera tempo da Javascript con performance.now()o da Python contime.time()


32
Un'aggiunta importante è che non puoi mai forzare un browser a fare qualcosa. Tutto quello che puoi fare è dare suggerimenti amichevoli. Spetta al browser e all'utente seguire effettivamente questi suggerimenti. Un browser è libero di ignorarlo o un utente può ignorare le impostazioni predefinite.
Joel Coehoorn

8
Joel, sarebbe stato meglio aggiungerlo nella tua risposta.
Epochwolf il

1
Solo un pensiero, e troppo tardi per venire alla festa qui, ma poiché hai il controllo della modifica dell'immagine, forse sarebbe meglio rinominare l'immagine mentre viene aggiornata, invece di aggiungere una stringa di query. Quindi: 1222259157.jpg per esempio, invece di picture.jpg? 1222259157. In questo modo viene aggiornato e memorizzato nuovamente nella cache dopo una nuova visita.
danjah

43
Invece di aggiungere l'ora del server, che impedirà del tutto la memorizzazione nella cache, perché non aggiungere l'ora dell'ultima modifica del file dopo "?". In questo modo, l'immagine verrà memorizzata normalmente nella cache fino alla successiva modifica.
Doin

3
L'ultimo commento di Doin deve essere votato positivamente! Il caching intelligente è davvero importante, perché qualcuno dovrebbe scaricare più e più volte lo stesso file?
f.arcarese

46

Soluzione semplice: allegare una stringa di query casuale all'immagine:

<img src="foo.cgi?random=323527528432525.24234" alt="">

Cosa dice la RFC HTTP:

Cache-Control: no-cache

Ma non funziona così bene :)


1
come dare Cache-Control: no-cache nel normale tag html <img>?
P Satish Patro

Deve essere inserito nell'intestazione della risposta?
P Satish Patro

22

Uso la funzione ora modificata del file di PHP , ad esempio:

echo <img  src='Images/image.png?" . filemtime('Images/image.png') . "'  />";

Se modifichi l'immagine, viene utilizzata la nuova immagine anziché quella memorizzata nella cache, a causa del diverso timestamp modificato.


Ottimo suggerimento. Hai sia il caching che l'immagine rispediti al client se l'ora modificata cambia (sovrascritta o modificata).
Vasilis Lourdas

vedi qui per un'altra opzione stackoverflow.com/questions/56588850/...
drooh

Absolute l'ha inchiodato. Ottimo compagno di punta.
Khurram Shaikh

Perfetto ! Solo un suggerimento in più: <img src = "images / image.png? <? Php echo filemtime ('images / image.jpg')?>">
Rica Gurgel

16

Io userei:

<img src="picture.jpg?20130910043254">

dove "20130910043254" è l'ora di modifica del file.

Quando si carica un'immagine, il suo nome file non viene mantenuto nel database. Viene rinominato come Image.jpg (per semplificare le cose quando lo si utilizza). Quando si sostituisce l'immagine esistente con una nuova, neanche il nome cambia. Cambia solo il contenuto del file immagine.

Penso che ci siano due tipi di soluzioni semplici: 1) quelle che vengono in mente per prime (soluzioni dirette, perché sono facili da trovare), 2) quelle che si ottengono dopo aver riflettuto sulle cose (perché sono facili da trovare uso). Apparentemente, non trarrai sempre vantaggio se scegli di riflettere sulle cose. Ma la seconda opzione è piuttosto sottovalutata, credo. Pensa solo perché phpè così popolare;)


1
+1 Penso che sia un'idea molto migliore rispetto ad altre risposte perché usando il tempo di modifica las, il tuo server non proverà a dare la stessa immagine più volte allo stesso browser quando non è cambiato nulla nell'immagine. C'è un piccolo sovraccarico per estrarre l'ora dell'ultima scrittura dell'immagine ogni volta che c'è una richiesta, ma per un'immagine grande, è meglio che dover restituire l'immagine ogni volta e quando l'immagine cambia, l'utente avrà quella nuova. Grazie per questa bella piccola aggiunta.
Samuel,

Bene, si potrebbe memorizzare il tempo di modifica insieme al percorso dell'immagine nel database. Quindi il sovraccarico potrebbe essere ancora meno significativo. D'altra parte se si tratta di immagini che fanno parte del codice sorgente di cui stiamo parlando, si potrebbero memorizzare anche i tempi di modifica. Generando uno script con i tempi di modifica delle immagini (es images.php.). Questo script deve essere rigenerato a ogni commit ed elimina il sovraccarico di determinare i tempi di modifica dei file.
x-yuri

@ x-Yori è per questo che sto scegliendo di conservare questo campo aggiornato nel database, piuttosto che correre filemtime stackoverflow.com/questions/56588850/...
drooh

9

usa Class = "NO-CACHE"

html di esempio:

<div>
    <img class="NO-CACHE" src="images/img1.jpg" />
    <img class="NO-CACHE" src="images/imgLogo.jpg" />
</div>

jQuery:

    $(document).ready(function ()
    {           
        $('.NO-CACHE').attr('src',function () { return $(this).attr('src') + "?a=" + Math.random() });
    });

javascript:

var nods = document.getElementsByClassName('NO-CACHE');
for (var i = 0; i < nods.length; i++)
{
    nods[i].attributes['src'].value += "?a=" + Math.random();
}

Risultato: src = "images / img1.jpg" => src = "images / img1.jpg? A = 0,08749723793963926"


Semplice, elegante e non richiede rendering lato server. SIMPATICO!
Ahi Tuna

7

Puoi scrivere uno script proxy per fornire immagini, ma è un po 'più di lavoro. Qualcosa piace questo:

HTML:

<img src="image.php?img=imageFile.jpg&some-random-number-262376" />

script:

// PHP
if( isset( $_GET['img'] ) && is_file( IMG_PATH . $_GET['img'] ) ) {

  // read contents
  $f = open( IMG_PATH . $_GET['img'] );
  $img = $f.read();
  $f.close();

  // no-cache headers - complete set
  // these copied from [php.net/header][1], tested myself - works
  header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Some time in the past
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
  header("Cache-Control: no-store, no-cache, must-revalidate"); 
  header("Cache-Control: post-check=0, pre-check=0", false); 
  header("Pragma: no-cache"); 

  // image related headers
  header('Accept-Ranges: bytes');
  header('Content-Length: '.strlen( $img )); // How many bytes we're going to send
  header('Content-Type: image/jpeg'); // or image/png etc

  // actual image
  echo $img;
  exit();
}

In realtà, le intestazioni senza cache o il numero casuale nell'immagine src dovrebbero essere sufficienti, ma poiché vogliamo essere a prova di proiettile ..


La tua è una buona soluzione, tranne per il fatto che Pragma non è un'intestazione di risposta.
Piskvor ha lasciato l'edificio il

1
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 sembra dire il contrario.
Grizly

6

Sono un NUOVO programmatore, ma ecco cosa mi è venuto in mente, per impedire al browser di memorizzare nella cache e trattenere le visualizzazioni della mia webcam:

<meta Http-Equiv="Cache" content="no-cache">
<meta Http-Equiv="Pragma-Control" content="no-cache">
<meta Http-Equiv="Cache-directive" Content="no-cache">
<meta Http-Equiv="Pragma-directive" Content="no-cache">
<meta Http-Equiv="Cache-Control" Content="no-cache">
<meta Http-Equiv="Pragma" Content="no-cache">
<meta Http-Equiv="Expires" Content="0">
<meta Http-Equiv="Pragma-directive: no-cache">
<meta Http-Equiv="Cache-directive: no-cache">

Non sono sicuro di cosa funzioni su quale browser, ma funziona per alcuni: IE: funziona quando la pagina web viene aggiornata e quando il sito web viene rivisitato (senza un aggiornamento). CHROME: funziona solo quando la pagina web viene aggiornata (anche dopo una nuova visita). SAFARI e iPad: non funziona, devo cancellare la cronologia ei dati web.

Qualche idea su SAFARI / iPad?


4

Quando si carica un'immagine, il suo nome file non viene mantenuto nel database. Viene rinominato come Image.jpg (per semplificare le cose quando lo si utilizza).

Cambia questo e hai risolto il tuo problema. Uso i timestamp, come con le soluzioni proposte sopra: Image- <timestamp> .jpg

Presumibilmente, qualsiasi problema tu stia evitando mantenendo lo stesso nome file per l'immagine può essere superato, ma non dici cosa sono.


4

Ho controllato tutte le risposte sul web e la migliore sembrava essere: (in realtà non lo è)

<img src="image.png?cache=none">

All'inizio.

Tuttavia, se aggiungi cache = none parametro (che è una parola statica "none"), non ha alcun effetto, il browser viene comunque caricato dalla cache.

La soluzione a questo problema era:

<img src="image.png?nocache=<?php echo time(); ?>">

dove fondamentalmente aggiungi timestamp unix per rendere il parametro dinamico e senza cache, ha funzionato.

Tuttavia, il mio problema era leggermente diverso: stavo caricando al volo l'immagine del grafico php generato e controllando la pagina con i parametri $ _GET. Volevo che l'immagine venisse letta dalla cache quando il parametro URL GET rimane lo stesso e non la cache quando i parametri GET cambiano.

Per risolvere questo problema, avevo bisogno di hash $ _GET ma poiché è un array ecco la soluzione:

$chart_hash = md5(implode('-', $_GET));
echo "<img src='/images/mychart.png?hash=$chart_hash'>";

Modifica :

Sebbene la soluzione di cui sopra funzioni bene, a volte si desidera fornire la versione memorizzata nella cache FINO A CHE il file non viene modificato. (con la soluzione di cui sopra, disabilita completamente la cache per quell'immagine) Quindi, per servire l'immagine memorizzata nella cache dal browser FINO A CHE non ci sia una modifica nell'uso del file immagine:

echo "<img src='/images/mychart.png?hash=" . filemtime('mychart.png') . "'>";

filemtime () ottiene l'ora di modifica del file.


2
La filemtimesoluzione è migliore anche perché md5richiede molta potenza di elaborazione.
oriadam

2
Ho passato giorni a cercare di ottenere l'app basata su Chromium per interrompere la memorizzazione nella cache delle immagini. Il? Nocache con l'eco del tempo ha risolto il problema. Grazie!
Woody

2

Il tuo problema è che, nonostante l' Expires:intestazione, il tuo browser sta riutilizzando la sua copia in memoria dell'immagine da prima che fosse aggiornata, invece di controllare la sua cache.

Ho avuto una situazione molto simile durante il caricamento delle immagini del prodotto nel back-end di amministrazione per un sito simile a un negozio e nel mio caso ho deciso che l'opzione migliore era usare javascript per forzare un aggiornamento dell'immagine, senza utilizzare nessuna delle tecniche di modifica dell'URL di altre persone ho già menzionato qui. Invece, ho inserito l'URL dell'immagine in un IFRAME nascosto, chiamato location.reload(true)nella finestra dell'IFRAME e quindi sostituito la mia immagine sulla pagina. Ciò forza un aggiornamento dell'immagine, non solo sulla pagina in cui mi trovo, ma anche su tutte le pagine successive che visito, senza che il client o il server debbano ricordare alcun parametro URL querytring o identificatore di frammento.

Ho pubblicato un codice per farlo nella mia risposta qui .


2

Aggiungi un timestamp <img src="picture.jpg?t=<?php echo time();?>">

darà sempre al file un numero casuale alla fine e interromperà la memorizzazione nella cache


Vale la pena notare che l'implementazione di questa soluzione pone stress sul server per richiesta e non funzionerà necessariamente poiché il browser può memorizzare nella cache la pagina php che contiene il tempo generato al momento della richiesta iniziale che viene memorizzata nella cache. Ciò funzionerebbe in modo affidabile solo se anche la pagina avesse una stringa di query dinamica.
Dmitry

1

Con il potenziale per proxy trasparenti mal funzionanti tra te e il client, l'unico modo per garantire totalmente che le immagini non verranno memorizzate nella cache è di dare loro un uri univoco, qualcosa come contrassegnare un timestamp come stringa di query o come parte del sentiero.

Se il timestamp corrisponde all'ora dell'ultimo aggiornamento dell'immagine, puoi memorizzare nella cache quando necessario e pubblicare la nuova immagine al momento giusto.


1

Presumo che la domanda originale riguardi le immagini memorizzate con alcune informazioni di testo. Quindi, se hai accesso a un contesto di testo durante la generazione di src = ... url, considera di memorizzare / utilizzare CRC32 di byte immagine invece di casuale o timestamp senza significato. Quindi, se viene visualizzata la pagina con molte immagini, verranno ricaricate solo le immagini aggiornate. Alla fine, se la memorizzazione CRC è impossibile, può essere calcolata e aggiunta all'URL in fase di esecuzione.


Oppure usa l'ora dell'ultima modifica dell'immagine, invece di un CRC, ancora meglio!
Doin

1

Dal mio punto di vista, disabilitare il caching delle immagini è una cattiva idea. Affatto.

Il problema principale qui è: come forzare il browser ad aggiornare l'immagine, quando è stata aggiornata sul lato server.

Anche in questo caso, dal mio punto di vista personale, la soluzione migliore è disabilitare l'accesso diretto alle immagini. Accedi invece alle immagini tramite filtro lato server / servlet / altri strumenti / servizi simili.

Nel mio caso è un servizio di riposo, che restituisce l'immagine e allega ETag in risposta. Il servizio mantiene l'hash di tutti i file, se il file viene modificato, l'hash viene aggiornato. Funziona perfettamente con tutti i browser moderni. Sì, ci vuole tempo per implementarlo, ma ne vale la pena.

L'unica eccezione sono le favicon. Per alcuni motivi non funziona. Non ho potuto forzare il browser ad aggiornare la sua cache dal lato server. ETags, Cache Control, Expires, Pragma header, niente ha aiutato.

In questo caso, l'aggiunta di alcuni parametri random / version nell'URL, a quanto pare, è l'unica soluzione.


1

Idealmente, dovresti aggiungere un pulsante / associazione di tasti / menu a ciascuna pagina web con un'opzione per sincronizzare il contenuto.

Per fare ciò, dovresti tenere traccia delle risorse che potrebbero dover essere sincronizzate e utilizzare xhr per sondare le immagini con una stringa di query dinamica o creare un'immagine in fase di esecuzione con src utilizzando una stringa di query dinamica. Quindi utilizzare un meccanismo di trasmissione per notificare a tutti i componenti delle pagine Web che utilizzano la risorsa di aggiornarsi per utilizzare la risorsa con una stringa di query dinamica aggiunta al suo URL.

Un esempio ingenuo è simile a questo:

Normalmente, l'immagine viene visualizzata e memorizzata nella cache, ma se l'utente preme il pulsante, viene inviata una richiesta xhr alla risorsa con una stringa di query ora aggiunta ad essa; poiché il tempo può essere assunto diverso su ogni stampa, farà in modo che il browser aggiri la cache poiché non può dire se la risorsa è generata dinamicamente sul lato server in base alla query, o se è statica risorsa che ignora la query.

Il risultato è che puoi evitare che tutti i tuoi utenti ti bombardino continuamente di richieste di risorse, ma allo stesso tempo, consenti agli utenti di aggiornare le loro risorse se sospettano che non siano sincronizzate.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="mobile-web-app-capable" content="yes" />        
    <title>Resource Synchronization Test</title>
    <script>
function sync() {
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {            
            var images = document.getElementsByClassName("depends-on-resource");

            for (var i = 0; i < images.length; ++i) {
                var image = images[i];
                if (image.getAttribute('data-resource-name') == 'resource.bmp') {
                    image.src = 'resource.bmp?i=' + new Date().getTime();                
                }
            }
        }
    }
    xhr.open('GET', 'resource.bmp', true);
    xhr.send();
}
    </script>
  </head>
  <body>
    <img class="depends-on-resource" data-resource-name="resource.bmp" src="resource.bmp"></img>
    <button onclick="sync()">sync</button>
  </body>
</html>


0

È necessario utilizzare uno o più nomi di file univoci. Come questo

<img src="cars.png?1287361287" alt="">

Ma questa tecnica significa un elevato utilizzo del server e uno spreco di larghezza di banda. Invece, dovresti usare il numero di versione o la data. Esempio:

<img src="cars.png?2020-02-18" alt="">

Ma vuoi che non serva mai immagini dalla cache. Per questo, se la pagina non utilizza la cache della pagina , è possibile con PHP o lato server.

<img src="cars.png?<?php echo time();?>" alt="">

Tuttavia, non è ancora efficace. Motivo: cache del browser ... L'ultimo ma più efficace metodo è Native JAVASCRIPT. Questo semplice codice trova tutte le immagini con una classe "NO-CACHE" e rende le immagini quasi uniche. Inseriscilo tra i tag di script.

var items = document.querySelectorAll("img.NO-CACHE");
for (var i = items.length; i--;) {
    var img = items[i];
    img.src = img.src + '?' + Date.now();
}

USO

<img class="NO-CACHE" src="https://upload.wikimedia.org/wikipedia/commons/6/6a/JavaScript-logo.png" alt="">

RISULTATO (i) come questo

https://example.com/image.png?1582018163634
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.