So erzwingen Sie, dass ein Webbrowser KEINE Bilder zwischenspeichert


124

Hintergrund

Ich schreibe und verwende ein sehr einfaches CGI-basiertes (Perl) Content-Management-Tool für zwei Pro-Bono-Websites. Es stellt dem Website-Administrator HTML-Formulare für Ereignisse zur Verfügung, in denen die Felder (Datum, Ort, Titel, Beschreibung, Links usw.) ausgefüllt und gespeichert werden. Auf diesem Formular erlaube ich dem Administrator, ein Bild hochzuladen, das sich auf das Ereignis bezieht. Auf der HTML-Seite, auf der das Formular angezeigt wird, wird auch eine Vorschau des hochgeladenen Bildes angezeigt (HTML-IMG-Tag).

Das Problem

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

Sobald das Bild hochgeladen wurde, ü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 BILD im Webbrowser zwischengespeichert wird. Wenn der Administrator in Firefox / Explorer / Safari auf die Schaltfläche RELOAD klickt, wird alles ordnungsgemäß aktualisiert und das neue Bild wird nur angezeigt.

Meine Lösung - funktioniert nicht

Ich versuche, den Cache durch Schreiben einer HTTP Expires-Anweisung mit einem Datum zu steuern, das sehr 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.

Dies funktioniert aber auch nicht.

Anmerkungen

Beim Hochladen eines Bildes wird dessen Dateiname nicht in der Datenbank gespeichert. Es wird als umbenannt Image.jpg (einfach Dinge bei der Verwendung von it). 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 verwendet Apache.

Frage

Gibt es eine Möglichkeit, den Webbrowser zu zwingen, KEINE Dinge 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 der Quellcode 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 immer noch nicht, wenn das neu hochgeladene Bild denselben Namen hat (sagen wir, das Bild wird ein wenig mit Photoshops versehen und erneut hochgeladen).


Ich denke, diese Frage könnte überarbeitet werden, um all den "jetzt nutzlosen Kontext" zu entfernen, den ich vor einiger Zeit dort eingefügt habe, als ich ihn geschrieben habe. Ich denke, der Titel ist der richtige und auch die beste Antwort. Aber die Frage wurde ursprünglich geschrieben, um viel Raum für viele Arten von Antworten zu bieten, und hätte eine so einfache Lösung nicht erwarten können. Daher ist die Frage für die Leute, die dort hereinkommen, etwas kompliziert, um die Antwort zu erhalten.
Philibert Perusse

Antworten:


186

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

Wobei "1222259157.415" die aktuelle Zeit auf dem Server ist.
Generieren Sie Zeit mit Javascript mit performance.now()oder mit Python mittime.time()


32
Eine wichtige Ergänzung ist, dass Sie einen Browser niemals zu irgendetwas zwingen können. Sie können nur freundliche Vorschläge machen. Es ist Sache des Browsers und des Benutzers, diesen Vorschlägen tatsächlich zu folgen. Ein Browser kann dies ignorieren, oder ein Benutzer kann die Standardeinstellungen überschreiben.
Joel Coehoorn

8
Joel, du hättest das besser in deine eigene Antwort aufgenommen.
Epochwolf

1
Nur ein Gedanke und viel zu spät, um hier zur Party zu kommen - aber da Sie die Kontrolle über die Bildänderung haben, ist es vielleicht besser, das Bild beim Aktualisieren umzubenennen, anstatt eine Abfragezeichenfolge hinzuzufügen. Also: 1222259157.jpg zum Beispiel anstelle von picture.jpg? 1222259157. Auf diese Weise wird es aktualisiert und bei einem erneuten Besuch erneut zwischengespeichert.
Danjah

43
Anstatt die Serverzeit hinzuzufügen, die das Caching insgesamt verhindert, können Sie die zuletzt geänderte Zeit der Datei nach dem '?' Auf diese Weise wird das Bild normal zwischengespeichert, bis es sich das nächste Mal ändert.
Doin

3
Der letzte Kommentar von Doin muss positiv bewertet werden! Smart Caching ist in der Tat wichtig, warum jemand immer wieder dieselbe Datei herunterladen müsste?
f.arcarese

46

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 :)


1
Wie gebe ich Cache-Control: kein Cache im normalen HTML-Tag <img>?
P Satish Patro

Muss es im Antwortheader stehen?
P Satish Patro

22

Ich verwende die dateimodifizierte Zeitfunktion von PHP , zum Beispiel:

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

Wenn Sie das Bild ändern, wird das neue Bild anstelle des zwischengespeicherten verwendet, da ein anderer geänderter Zeitstempel vorhanden ist.


Toller Tipp. Sie haben sowohl das Caching als auch das Image erneut an den Client gesendet, wenn sich die geänderte Zeit ändert (überschrieben oder geändert).
Vasilis Lourdas

siehe hier für eine andere Option stackoverflow.com/questions/56588850/…
drooh

Absolut genagelt. Toller Tipp Kumpel.
Khurram Shaikh

Perfekt ! Nur ein zusätzlicher Tipp: <img src = "images / image.png? <? Php echo filemtime ('images / image.jpg')?>">
Rica Gurgel

16

Ich würde ... benutzen:

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

Dabei ist "20130910043254" die Änderungszeit der Datei.

Beim Hochladen eines Bildes wird dessen Dateiname nicht in der Datenbank gespeichert. Es wird in Image.jpg umbenannt (um die Dinge bei der Verwendung einfach zu ändern). 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) diejenigen, die zuerst in den Sinn kommen (einfache Lösungen, weil sie leicht zu finden sind), 2) diejenigen, die Sie am Ende haben, nachdem Sie über die Dinge nachgedacht haben (weil sie leicht zu finden sind) verwenden). Anscheinend werden Sie nicht immer davon profitieren, wenn Sie sich entscheiden, über Dinge nachzudenken. Aber die zweite Option wird meiner Meinung nach eher unterschätzt. Denken Sie nur, warum phpist so beliebt;)


1
+1 Ich denke, es ist eine viel bessere Idee als andere Antworten, da Ihr Server bei Verwendung der Las-Änderungszeit nicht versucht, dasselbe Bild mehrmals an denselben Browser zu senden, wenn sich nichts am Bild geändert hat. Das Extrahieren der letzten Schreibzeit des Bildes bei jeder Anforderung ist mit einem geringen Aufwand verbunden. Bei einem großen Bild ist es jedoch besser, das Bild jedes Mal zurückzugeben, und wenn sich das Bild ändert, hat der Benutzer das neue. Vielen Dank für diese nette kleine Ergänzung.
Samuel

Nun, man könnte die Änderungszeit zusammen mit dem Pfad zum Bild in der Datenbank speichern. Der Overhead könnte also noch geringer sein. Wenn es sich jedoch um Bilder handelt, die Teil des Quellcodes sind, über den wir sprechen, können auch deren Änderungszeiten zwischengespeichert werden. Durch Generieren eines Skripts mit Änderungszeiten von Bildern (z images.php. B. ). Dieses Skript muss bei jedem Commit neu generiert werden und beseitigt den Aufwand für die Ermittlung der Änderungszeiten von Dateien.
X-Yuri

@ x-yori aus diesem Grund entscheide ich mich, dieses aktualisierte Feld in der Datenbank zu speichern, anstatt die Dateizeit zu starten. stackoverflow.com/questions/56588850/…
drooh

9

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"


Einfach, elegant und erfordert kein serverseitiges Rendern. NETT!
Ahi Thunfisch

7

Sie können ein Proxy-Skript zum Bereitstellen von Bildern schreiben - das ist jedoch etwas 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 sollten entweder No-Cache-Header oder Zufallszahlen bei image src ausreichen, aber da wir kugelsicher sein wollen ..


Ihre ist eine gute Lösung, außer dass Pragma kein Antwortheader ist.
Piskvor verließ das Gebäude am


6

Ich bin ein NEUER Codierer, aber ich habe mir Folgendes ausgedacht, um zu verhindern, dass der Browser meine Webcam-Ansichten zwischenspeichert und festhält:

<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 in welchem ​​Browser funktioniert, aber es funktioniert bei einigen: IE: Funktioniert, wenn die Webseite aktualisiert wird und wenn die Website erneut aufgerufen wird (ohne Aktualisierung). CHROM: 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?


4

Beim Hochladen eines Bildes wird dessen Dateiname nicht in der Datenbank gespeichert. Es wird in Image.jpg umbenannt (um die Dinge bei der Verwendung einfach zu ändern).

Ä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 denselben Dateinamen für das Bild beibehalten, behoben werden, aber Sie sagen nicht, was sie sind.


4

Ich habe alle Antworten im Internet überprüft und die beste schien zu sein: (eigentlich ist es nicht)

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

zunaechst.

Wenn Sie jedoch den Parameter cache = none hinzufügen (dies ist das statische Wort "none"), 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 einen Unix-Zeitstempel hinzufügen, um den Parameter dynamisch und ohne Cache zu machen, hat es funktioniert.

Mein Problem war jedoch etwas anders: Ich lud im laufenden Betrieb ein generiertes PHP-Diagrammbild und steuerte die Seite mit $ _GET-Parametern. 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 sich um ein Array handelt, 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 zwischengespeicherte Bilder vom Browser bereitzustellen, BIS es eine Änderung in der Verwendung der Bilddatei gibt:

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

filemtime () erhält die Änderungszeit der Datei.


2
Die filemtimeLösung ist auch deshalb besser, weil sie md5viel Rechenleistung benötigt.
Oriadam

2
Verbrachte Tage damit, die Chromium-basierte App dazu zu bringen, das Zwischenspeichern von Bildern zu beenden. Der? Nocache mit Zeitecho löste das Problem. Danke dir!
Woody

2

Ihr Problem ist, dass Expires:Ihr Browser trotz des Headers die speicherinterne Kopie des Bildes von vor der Aktualisierung wiederverwendet, anstatt sogar den Cache zu überprüfen.

Ich hatte eine sehr ähnliche Situation beim Hochladen von Produktbildern im Admin-Backend für eine Store-ähnliche Site. In meinem Fall entschied ich, dass die beste Option darin bestand, Javascript zu verwenden, 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 einen versteckten IFRAME eingefügt, location.reload(true)der im IFRAME-Fenster aufgerufen wurde, 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 späteren Seiten, die ich besuche - ohne dass sich Client oder Server an URL-Querystring- oder Fragment-ID-Parameter erinnern müssen.

Ich habe in meiner Antwort einen Code dazu gepostet hier .


2

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

Geben Sie Ihrer Datei am Ende immer eine Zufallszahl und stoppen Sie das Caching


Es ist erwähnenswert, dass die Implementierung dieser Lösung den Server pro Anforderung belastet und nicht unbedingt funktioniert, da der Browser die PHP-Seite zwischenspeichern kann, die die Zeit enthält, die zum Zeitpunkt der ersten Anforderung generiert wurde, die zwischengespeichert wird. Dies würde nur zuverlässig funktionieren, wenn auch die Seite eine dynamische Abfragezeichenfolge hätte.
Dmitry

1

Mit dem Potenzial für schlecht benommene transparente Proxys zwischen Ihnen und dem Client besteht die einzige Möglichkeit, um sicherzustellen, dass Bilder nicht zwischengespeichert werden, darin, ihnen eine eindeutige URL zu geben, beispielsweise das Markieren eines Zeitstempels als Abfragezeichenfolge oder als Teil des Pfad.

Wenn dieser Zeitstempel der letzten Aktualisierungszeit des Bildes entspricht, können Sie das neue Bild bei Bedarf zwischenspeichern und zum richtigen Zeitpunkt bereitstellen.


1

Ich gehe davon aus, dass die ursprüngliche Frage Bilder betrifft, die mit einigen 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. Wenn dann die Seite mit vielen Bildern angezeigt wird, werden nur aktualisierte Bilder neu geladen. Wenn eine CRC-Speicherung nicht möglich ist, kann sie zur Laufzeit berechnet und an die URL angehängt werden.


Oder verwenden Sie die zuletzt geänderte Zeit des Bildes anstelle eines CRC, noch besser!
Doin

1

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 Bild zu aktualisieren, wenn es auf einer Serverseite aktualisiert wurde.

Aus meiner persönlichen Sicht besteht die beste Lösung darin, den direkten Zugriff auf Bilder zu deaktivieren. Greifen Sie stattdessen über serverseitige Filter / Servlets / ähnliche Tools / Dienste auf Bilder zu.

In meinem Fall handelt es sich um einen Restdienst, der ein Bild zurückgibt und als Antwort ETag anfügt. Der Dienst speichert den Hash aller Dateien. Wenn die Datei geändert wird, wird der Hash aktualisiert. Es funktioniert perfekt in allen modernen Browsern. Ja, die Implementierung braucht Zeit, aber es lohnt sich.

Die einzige Ausnahme - sind Favoriten. Aus bestimmten 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 ist das Hinzufügen eines Zufalls- / Versionsparameters zur URL anscheinend die einzige Lösung.


1

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

Zu diesem Zweck würden Sie die Ressourcen verfolgen, die möglicherweise synchronisiert werden müssen, und entweder xhr verwenden, um die Bilder mit einem dynamischen Querystring zu prüfen, oder zur Laufzeit mit src ein Bild mit einem dynamischen Querystring erstellen. Verwenden Sie dann einen Broadcast-Mechanismus, um alle Komponenten der Webseiten zu benachrichtigen, die die Ressource zum Aktualisieren verwenden, um die Ressource mit einem dynamischen Querystring zu verwenden, der an ihre URL angehängt ist.

Ein naives Beispiel sieht so aus:

Normalerweise wird das Bild angezeigt und zwischengespeichert. Wenn der Benutzer jedoch die Taste gedrückt hat, wird eine xhr-Anforderung an die Ressource gesendet, an die eine Zeitabfrage angehängt ist. Da davon ausgegangen werden kann, dass die Zeit bei jedem Drücken unterschiedlich ist, wird sichergestellt, dass der Browser den Cache umgeht, da er nicht erkennen kann, ob die Ressource auf der Serverseite basierend auf der Abfrage dynamisch generiert wird oder ob sie statisch ist Ressource, die die Abfrage ignoriert.

Das Ergebnis ist, dass Sie vermeiden können, dass alle Benutzer Sie ständig mit Ressourcenanforderungen bombardieren, aber gleichzeitig einen Mechanismus zulassen, mit dem Benutzer ihre Ressourcen aktualisieren können, 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

Sie müssen einen oder mehrere eindeutige Dateinamen verwenden. So was

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

Diese Technik bedeutet jedoch eine hohe Servernutzung und Bandbreitenverschwendung. Verwenden Sie stattdessen die Versionsnummer oder das Datum. Beispiel:

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

Sie möchten jedoch, dass niemals Bilder aus dem Cache bereitgestellt werden. Dazu , wenn die Seite nicht Seiten - Cache verwendet , ist es möglich , mit PHP oder Server - Seite.

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

Es ist jedoch immer noch nicht wirksam. Grund: Browser-Cache ... Die letzte, aber effektivste Methode ist Native JAVASCRIPT. Dieser einfache Code findet alle Bilder mit einer "NO-CACHE" -Klasse und macht die Bilder fast einzigartig. Fügen Sie dies zwischen Skript-Tags ein.

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

VERWENDUNG

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

ERGEBNIS (e) wie folgt

https://example.com/image.png?1582018163634
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.