web-dev-qa-db-de.com

So erzwingen Sie, dass ein Webbrowser Bilder NICHT zwischenspeichert

Hintergrund

Ich schreibe und benutze ein sehr einfaches CGI-basiertes (Perl) Content-Management-Tool für zwei Pro-Bono-Websites. Der Administrator der Website erhält HTML-Formulare für Ereignisse, in denen die Felder (Datum, Ort, Titel, Beschreibung, Links usw.) ausgefüllt und gespeichert werden. Auf diesem Formular erlaube ich dem Administrator, ein Bild zum Ereignis hochzuladen. Auf der HTML-Seite, auf der das Formular angezeigt wird, wird auch eine Vorschau des hochgeladenen Bilds angezeigt (HTML-img-Tag).

Das Problem

Das Problem tritt auf, wenn der Administrator das Bild ändern möchte. Er müsste nur auf die Schaltfläche "Durchsuchen" klicken, ein neues Bild auswählen und auf OK klicken. Und das funktioniert gut.

Sobald das Bild hochgeladen ist, übernimmt mein Back-End-CGI den Upload und lädt das Formular ordnungsgemäß neu.

Das Problem ist, dass das gezeigte Bild nicht aktualisiert wird. Das alte Bild wird weiterhin angezeigt, obwohl die Datenbank das richtige Bild enthält. Ich habe es auf die Tatsache eingegrenzt, dass das IMAGE IS CACHED im Webbrowser. Wenn der Administrator in Firefox/Explorer/Safari auf die Schaltfläche RELOAD klickt, wird alles in Ordnung aktualisiert und das neue Image nur erscheint.

Meine Lösung - funktioniert nicht

Ich versuche, den Cache zu kontrollieren, indem ich eine HTTP Expires-Anweisung mit einem Datum schreibe, das weit in der Vergangenheit liegt.

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

Denken Sie daran, dass ich auf der administrativen Seite bin und es mir egal ist, ob das Laden der Seiten etwas länger dauert, da sie immer abgelaufen sind.

Das funktioniert aber auch nicht.

Anmerkungen

Beim Hochladen eines Bildes wird der Dateiname nicht in der Datenbank gespeichert. Es wird umbenannt in Image.jpg (um es bei der Verwendung einfach auszublenden). Wenn Sie das vorhandene Bild durch ein neues ersetzen, ändert sich auch der Name nicht. Nur der Inhalt der Bilddatei ändert sich.

Der Webserver wird vom Hosting-Service/ISP bereitgestellt. Es benutzt Apache.

Frage

Gibt es eine Möglichkeit, den Webbrowser dazu zu zwingen, KEINE Daten von dieser Seite zwischenzuspeichern, nicht einmal Bilder?

Ich jongliere mit der Option, den Dateinamen tatsächlich mit der Datenbank zu "speichern". Auf diese Weise ändert sich auch die Quelle des IMG-Tags, wenn das Bild geändert wird. Dies erfordert jedoch viele Änderungen auf der gesamten Website und ich mache es lieber nicht, wenn ich eine bessere Lösung habe. Dies funktioniert auch dann nicht, wenn das neu hochgeladene Bild denselben Namen hat (z. B. wenn das Bild ein wenig photoshoppt und erneut hochgeladen wird).

113

Armin Ronacher hat die richtige Idee. Das Problem ist, dass zufällige Zeichenfolgen kollidieren können. Ich würde ... benutzen:

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

Dabei ist "1222259157.415" die aktuelle Uhrzeit auf dem Server.
Generiere Zeit mit Javascript mit performance.now() oder mit Python mit time.time()

171
epochwolf

Einfache Lösung: Fügen Sie dem Bild eine zufällige Abfragezeichenfolge hinzu:

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

Was der HTTP RFC sagt:

Cache-Control: no-cache

Aber das funktioniert nicht so gut :)

45
Armin Ronacher

Ich benutze PHPs dateimodifizierte Zeitfunktion , zum Beispiel:

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

Wenn Sie das Bild ändern, wird das neue Bild verwendet und nicht das zwischengespeicherte, da der Zeitstempel anders geändert wurde.

18
Rick

Ich würde ... benutzen:

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

dabei ist "20130910043254" die Änderungszeit der Datei.

Beim Hochladen eines Bildes wird der Dateiname nicht in der Datenbank gespeichert. Es wird in Image.jpg umbenannt (um es bei der Verwendung einfach auszublenden). Wenn Sie das vorhandene Bild durch ein neues ersetzen, ändert sich auch der Name nicht. Nur der Inhalt der Bilddatei ändert sich.

Ich denke, es gibt zwei Arten von einfachen Lösungen: 1) die, die zuerst in den Sinn kommen (einfache Lösungen, weil sie leicht zu finden sind), 2) die, mit denen Sie enden, nachdem Sie über Dinge nachgedacht haben (weil sie leicht zu finden sind) verwenden). Anscheinend werden Sie nicht immer davon profitieren, wenn Sie sich dazu entschließen, Dinge zu überdenken. Aber die zweite Option wird meiner Meinung nach eher unterschätzt. Denken Sie nur, warum php so beliebt ist;)

15
x-yuri

Sie können ein Proxy-Skript zum Bereitstellen von Bildern schreiben - das ist jedoch ein bisschen mehr Arbeit. So etwas gefällt mir:

HTML:

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

Skript:

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

Eigentlich sollte entweder kein Cache-Header oder eine Zufallszahl bei Bild-Quellcode ausreichen, aber da wollen wir kugelsicher sein.

7
Mindeh

Ich bin ein NEUER Coder, aber hier ist, was ich mir ausgedacht habe, um den Browser daran zu hindern, meine Webcam-Ansichten zu cachen und festzuhalten:

<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">

Ich bin nicht sicher, was mit welchem ​​Browser funktioniert, aber bei einigen funktioniert es: IE: Funktioniert, wenn die Webseite aktualisiert wird und wenn die Website erneut besucht wird (ohne Aktualisierung). CHROME: Funktioniert nur, wenn die Webseite aktualisiert wird (auch nach einem erneuten Besuch). SAFARI und iPad: Funktioniert nicht, ich muss den Verlauf und die Webdaten löschen.

Irgendwelche Ideen auf SAFARI/iPad?

6
Timmy T.

benutze Class = "NO-CACHE"

beispiel-HTML:

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

Ergebnis: src = "images/img1.jpg" => src = "images/img1.jpg? A = 0.08749723793963926"

6

Ich habe alle Antworten im Internet überprüft und die beste schien zu sein:

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

zunaechst.

Wenn Sie jedoch den Parameter cache = none (bei dem es sich um ein statisches Wort "none" handelt) hinzufügen, hat dies keine Auswirkungen. Der Browser wird weiterhin aus dem Cache geladen.

Die Lösung für dieses Problem war:

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

wo Sie im Grunde genommen Unix-Zeitstempel hinzufügen, um den Parameter dynamisch und ohne Cache zu machen, hat es funktioniert.

Mein Problem war jedoch ein wenig anders: Ich habe ein im laufenden Betrieb erzeugtes PHP-Chart-Bild geladen und die Seite mit $ _GET-Parametern gesteuert. Ich wollte, dass das Bild aus dem Cache gelesen wird, wenn der URL-GET-Parameter gleich bleibt, und nicht zwischengespeichert wird, wenn sich die GET-Parameter ändern.

Um dieses Problem zu lösen, musste ich $ _GET hashen, aber da es ein Array ist, ist hier die Lösung:

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

Bearbeiten:

Obwohl die obige Lösung einwandfrei funktioniert, möchten Sie manchmal die zwischengespeicherte Version bereitstellen, BIS die Datei geändert wird. (Mit der obigen Lösung wird der Cache für dieses Bild vollständig deaktiviert.) Um das zwischengespeicherte Bild vom Browser BIS zu liefern, wurde die Verwendung der Bilddatei geändert:

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

filemtime () ruft die Änderungszeit der Datei ab.

4
Tarik

Beim Hochladen eines Bildes wird der Dateiname nicht in der Datenbank gespeichert. Es wird in Image.jpg umbenannt (um es bei der Verwendung einfach auszublenden).

Ändern Sie dies und Sie haben Ihr Problem behoben. Ich verwende Zeitstempel wie bei den oben vorgeschlagenen Lösungen: Image- <Zeitstempel> .jpg

Vermutlich können alle Probleme, die Sie vermeiden, indem Sie den gleichen Dateinamen für das Bild beibehalten, überwunden werden, aber Sie sagen nicht, was sie sind.

4
AmbroseChapel

Ihr Problem ist, dass Ihr Browser trotz des Headers Expires: Die speicherinterne Kopie des Bildes aus der Zeit vor der Aktualisierung wiederverwendet und nicht einmal den Cache überprüft.

Ich hatte eine sehr ähnliche Situation beim Hochladen von Produktbildern im Admin-Backend für eine store-ähnliche Site und in meinem Fall entschied ich, dass die beste Option die Verwendung von Javascript war, um eine Bildaktualisierung zu erzwingen, ohne eine der URL-Änderungstechniken anderer Personen zu verwenden habe hier schon erwähnt. Stattdessen habe ich die Bild-URL in einem versteckten IFRAME namens location.reload(true) im IFRAME-Fenster abgelegt und dann mein Bild auf der Seite ersetzt. Dies erzwingt eine Aktualisierung des Bildes, nicht nur auf der Seite, auf der ich mich befinde, sondern auch auf allen späteren Seiten, die ich besuche - ohne dass sich Client oder Server URL-Abfragezeichenfolgen oder Fragmentbezeichnerparameter merken müssen.

Ich habe dazu in meiner Antwort einen Code gepostet hier .

2
Doin

Zeitstempel hinzufügen <img src="picture.jpg?t=<?php echo time();?>">

gibt Ihrer Datei am Ende immer eine Zufallszahl und stoppt das Zwischenspeichern

2
BritishSam

Ich gehe davon aus, dass die ursprüngliche Frage Bilder betrifft, die mit Textinformationen gespeichert sind. Wenn Sie also beim Generieren von src = ... url Zugriff auf einen Textkontext haben, sollten Sie CRC32 von Bildbytes anstelle von bedeutungslosem Zufall oder Zeitstempel speichern/verwenden. Wenn dann die Seite mit vielen Bildern angezeigt wird, werden nur aktualisierte Bilder neu geladen. Wenn das Speichern von CRC nicht möglich ist, kann es zur Laufzeit berechnet und an die URL angehängt werden.

1
jbw

Da die Möglichkeit besteht, dass sich zwischen Ihnen und dem Client transparente Proxies schlecht verhalten, können Sie nur dann sicherstellen, dass die Bilder nicht zwischengespeichert werden, wenn Sie ihnen eine eindeutige URL geben, z. B. einen Zeitstempel als Abfragezeichenfolge oder als Teil der Pfad.

Wenn dieser Zeitstempel der letzten Aktualisierungszeit des Abbilds entspricht, können Sie das neue Abbild bei Bedarf zwischenspeichern und zur richtigen Zeit bereitstellen.

1
user7375

Aus meiner Sicht ist es eine schlechte Idee, das Zwischenspeichern von Bildern zu deaktivieren. Überhaupt.

Das Hauptproblem hierbei ist, wie der Browser gezwungen wird, das Image zu aktualisieren, wenn es serverseitig aktualisiert wurde.

Aus meiner persönlichen Sicht ist es die beste Lösung, den direkten Zugriff auf Bilder zu deaktivieren. Greifen Sie stattdessen über serverseitigen Filter/Servlet/andere ähnliche Tools/Dienste auf Bilder zu.

In meinem Fall handelt es sich um einen Rest-Service, der ein Image zurückgibt und als Antwort ETag anfügt. Der Dienst behält den Hash aller Dateien bei, wenn die Datei geändert wird, wird der Hash aktualisiert. Es funktioniert perfekt in allen modernen Browsern. Ja, es braucht Zeit, um es zu implementieren, aber es lohnt sich.

Die einzige Ausnahme - sind Favicons. Aus einigen Gründen funktioniert es nicht. Ich konnte den Browser nicht zwingen, seinen Cache von der Serverseite aus zu aktualisieren. ETags, Cache Control, Expires, Pragma-Header, nichts hat geholfen.

In diesem Fall scheint es die einzige Lösung zu sein, der URL einen Zufalls-/Versionsparameter hinzuzufügen.

1
Alexandr

Im Idealfall sollten Sie jeder Webseite eine Schaltfläche/Tastenkombination/ein Menü mit der Option zum Synchronisieren von Inhalten hinzufügen.

Zu diesem Zweck sollten Sie die Ressourcen verfolgen, die möglicherweise synchronisiert werden müssen, und entweder xhr verwenden, um die Images mit einer dynamischen Abfragezeichenfolge zu untersuchen, oder ein Image zur Laufzeit mit src mithilfe einer dynamischen Abfragezeichenfolge erstellen. Verwenden Sie anschließend einen Broadcast-Mechanismus, um alle Komponenten der Webseiten, die die Ressource verwenden, zu benachrichtigen, damit die Ressource mit einem dynamischen Abfrage-String verwendet wird, der an die URL angehängt wird.

Ein naives Beispiel sieht so aus:

Normalerweise wird das Bild angezeigt und zwischengespeichert. Wenn der Benutzer jedoch die Schaltfläche drückt, wird eine xhr-Anforderung mit einer angehängten Zeitabfrage an die Ressource gesendet. Da davon ausgegangen werden kann, dass die Zeit bei jedem Druck unterschiedlich ist, wird sichergestellt, dass der Browser den Cache umgeht, da nicht festgestellt werden kann, ob die Ressource auf der Serverseite basierend auf der Abfrage dynamisch generiert wird oder ob es sich um eine statische Ressource handelt Ressource, die die Abfrage ignoriert.

Das Ergebnis ist, dass Sie vermeiden können, dass alle Benutzer Sie ständig mit Ressourcenanforderungen bombardieren. Gleichzeitig können Benutzer ihre Ressourcen aktualisieren, wenn sie den Verdacht haben, dass sie nicht synchron sind.

<!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
Dmitry