Comment forcer un navigateur Web à NE PAS mettre en cache les images

124

Contexte

J'écris et j'utilise un outil de gestion de contenu basé sur CGI (Perl) très simple pour deux sites Web pro-bono. Il fournit à l'administrateur du site Web des formulaires HTML pour les événements où ils remplissent les champs (date, lieu, titre, description, liens, etc.) et les enregistrent. Sur ce formulaire, j'autorise l'administrateur à télécharger une image liée à l'événement. Sur la page HTML affichant le formulaire, j'affiche également un aperçu de l'image téléchargée (balise HTML img).

Le problème

Le problème se produit lorsque l'administrateur souhaite modifier l'image. Il lui suffirait d'appuyer sur le bouton "parcourir", de choisir une nouvelle image et d'appuyer sur ok. Et cela fonctionne très bien.

Une fois l'image téléchargée, mon CGI back-end gère le téléchargement et recharge le formulaire correctement.

Le problème est que l'image affichée ne se rafraîchit pas. L'ancienne image est toujours affichée, même si la base de données contient la bonne image. Je l'ai réduit au fait que l'IMAGE EST CACHÉE dans le navigateur Web. Si l'administrateur clique sur le bouton RELOAD dans Firefox / Explorer / Safari, tout est bien rafraîchi et la nouvelle image apparaît juste.

Ma solution - ne fonctionne pas

J'essaie de contrôler le cache en écrivant une instruction HTTP Expires avec une date très éloignée du passé.

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

Rappelez-vous que je suis du côté administratif et que je ne me soucie pas vraiment si les pages prennent un peu plus de temps à se charger car elles sont toujours expirées.

Mais cela ne fonctionne pas non plus.

Remarques

Lors du téléchargement d'une image, son nom de fichier n'est pas conservé dans la base de données. Il est renommé Image.jpg (pour simplement faire des choses lors de son utilisation). Lors du remplacement de l'image existante par une nouvelle, le nom ne change pas non plus. Seul le contenu du fichier image change.

Le serveur Web est fourni par le service d'hébergement / FAI. Il utilise Apache.

Question

Existe-t-il un moyen de forcer le navigateur Web à NE PAS mettre en cache des éléments de cette page, pas même des images?

Je jongle avec l'option de "sauvegarder le nom de fichier" avec la base de données. De cette façon, si l'image est modifiée, le src de la balise IMG changera également. Cependant, cela nécessite beaucoup de changements sur tout le site et je préfère ne pas le faire si j'ai une meilleure solution. De plus, cela ne fonctionnera toujours pas si la nouvelle image téléchargée porte le même nom (disons que l'image est un peu photoshoppée et retéléchargée).

Philibert Perusse
la source
Je pense que cette question pourrait être retravaillée pour supprimer tout le «contexte désormais inutile» que j'ai mis là-dedans il y a quelque temps quand je l'ai écrit. Je pense que le titre est le bon, et la meilleure réponse également. Mais la question a été rédigée à l'origine pour donner beaucoup de place à de nombreux types de réponses et n'aurait pas pu s'attendre à une solution aussi simple. Par conséquent, la question est un peu compliquée pour les personnes qui viennent ici pour obtenir la réponse.
Philibert Perusse

Réponses:

186

Armin Ronacher a la bonne idée. Le problème est que des chaînes aléatoires peuvent entrer en collision. J'utiliserais:

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

Où "1222259157.415" est l'heure actuelle sur le serveur.
Générer du temps par Javascript avec performance.now()ou par Python avectime.time()

epochwolf
la source
32
Un ajout important est que vous ne pouvez jamais forcer un navigateur à faire quoi que ce soit. Tout ce que vous pouvez faire est de faire des suggestions amicales. C'est au navigateur et à l'utilisateur de suivre réellement ces suggestions. Un navigateur est libre de l'ignorer, ou un utilisateur peut remplacer les valeurs par défaut.
Joel Coehoorn
8
Joel, tu aurais mieux fait d'ajouter cela dans ta propre réponse.
epochwolf
1
Juste une pensée, et beaucoup trop tard pour venir à la fête ici - mais puisque vous avez le contrôle de l'image changeante, il serait peut-être préférable de renommer l'image au fur et à mesure qu'elle est mise à jour, au lieu d'ajouter une chaîne de requête. Donc: 1222259157.jpg par exemple, au lieu de picture.jpg? 1222259157. De cette façon, il est mis à jour et remis en cache lors d'une nouvelle visite.
danjah
43
Plutôt que d'ajouter l'heure du serveur, ce qui empêchera complètement la mise en cache, pourquoi ne pas ajouter l'heure de dernière modification du fichier après le «?». De cette façon, l'image sera mise en cache normalement jusqu'à son prochain changement.
Fait
3
Le dernier commentaire de Doin doit être voté! La mise en cache intelligente est en effet importante, pourquoi quelqu'un devrait-il télécharger à plusieurs reprises le même fichier?
f.arcarese
46

Solution simple: associez une chaîne de requête aléatoire à l'image:

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

Ce que dit la RFC HTTP:

Cache-Control: no-cache

Mais cela ne fonctionne pas très bien :)

Armin Ronacher
la source
1
comment donner Cache-Control: no-cache dans la balise HTML <img> normale?
P Satish Patro
Doit-il figurer dans l'en-tête de la réponse?
P Satish Patro
22

J'utilise la fonction d'heure de modification de fichier de PHP , par exemple:

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

Si vous modifiez l'image, la nouvelle image est utilisée plutôt que celle mise en cache, en raison d'un horodatage modifié différent.

Meule
la source
Bon conseil. Vous avez à la fois la mise en cache et l'image renvoyées au client si son heure modifiée change (écrasée ou modifiée).
Vasilis Lourdas
voir ici pour une autre option stackoverflow.com/questions/56588850/…
drooh
Absolute l'a cloué. Excellent compagnon de pointe.
Khurram Shaikh
Parfait! Un petit conseil supplémentaire: <img src = "images / image.png? <? Php echo filemtime ('images / image.jpg')?>">
Rica Gurgel
16

J'utiliserais:

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

où "20130910043254" est l'heure de modification du fichier.

Lors du téléchargement d'une image, son nom de fichier n'est pas conservé dans la base de données. Il est renommé Image.jpg (pour simplement faire des choses lors de son utilisation). Lors du remplacement de l'image existante par une nouvelle, le nom ne change pas non plus. Seul le contenu du fichier image change.

Je pense qu'il y a deux types de solutions simples: 1) celles qui viennent à l'esprit en premier (solutions simples, parce qu'elles sont faciles à trouver), 2) celles avec lesquelles vous vous retrouvez après avoir réfléchi (parce qu'elles sont faciles à trouver) utilisation). Apparemment, vous ne bénéficierez pas toujours si vous choisissez de réfléchir. Mais la deuxième option est plutôt sous-estimée, je crois. Pensez simplement pourquoi phpest si populaire;)

x-yuri
la source
1
+1 Je pense que c'est une bien meilleure idée que les autres réponses car en utilisant l'heure de modification las, votre serveur n'essaiera pas de donner la même image plusieurs fois au même navigateur lorsque rien n'a changé dans l'image. Il y a un peu de surcharge pour extraire la dernière heure d'écriture de l'image à chaque fois qu'il y a une demande mais pour une grande image, c'est mieux que d'avoir à renvoyer l'image à chaque fois et lorsque l'image change, l'utilisateur aura la nouvelle. Merci pour ce joli petit ajout.
Samuel
Eh bien, on peut stocker l'heure de modification avec le chemin d'accès à l'image dans la base de données. Les frais généraux pourraient donc être encore moins importants. Par contre, si ce sont des images qui font partie du code source dont nous parlons, on peut aussi mettre en cache leurs heures de modification. En générant un script avec les temps de modification des images (par exemple images.php). Ce script doit être régénéré à chaque validation et élimine la surcharge liée à la détermination des heures de modification des fichiers.
x-yuri
@ x-yori c'est pourquoi je choisis de stocker ce champ mis à jour dans la base de données plutôt que d'exécuter filemtime stackoverflow.com/questions/56588850/…
drooh
9

utilisez Class = "NO-CACHE"

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

Résultat: src = "images / img1.jpg" => src = "images / img1.jpg? A = 0.08749723793963926"

Aref Rostamkhani
la source
Simple, élégant et ne nécessite pas de rendu côté serveur. AGRÉABLE!
Ahi Tuna
7

Vous pouvez écrire un script proxy pour servir des images - c'est un peu plus de travail cependant. Quelque chose aime ça:

HTML:

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

Scénario:

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

En fait, des en-têtes sans cache ou un nombre aléatoire à l'image src devraient être suffisants, mais puisque nous voulons être à l'épreuve des balles.

Mindeh
la source
La vôtre est une bonne solution, sauf que Pragma n'est pas un en-tête de réponse.
Piskvor a quitté le bâtiment le
1
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 semble dire le contraire.
Grizly
6

Je suis un NOUVEAU codeur, mais voici ce que j'ai proposé, pour empêcher le navigateur de se mettre en cache et de conserver les vues de ma 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">

Je ne sais pas ce qui fonctionne sur quel navigateur, mais cela fonctionne pour certains: IE: Fonctionne lorsque la page Web est actualisée et lorsque le site Web est revisité (sans actualisation). CHROME: fonctionne uniquement lorsque la page Web est actualisée (même après une nouvelle visite). SAFARI et iPad: ne fonctionne pas, je dois effacer l'historique et les données Web.

Des idées sur SAFARI / iPad?

Timmy T.
la source
4

Lors du téléchargement d'une image, son nom de fichier n'est pas conservé dans la base de données. Il est renommé Image.jpg (pour simplement faire des choses lors de son utilisation).

Changez cela et vous avez résolu votre problème. J'utilise des horodatages, comme pour les solutions proposées ci-dessus: Image- <horodatage> .jpg

Vraisemblablement, les problèmes que vous évitez en gardant le même nom de fichier pour l'image peuvent être surmontés, mais vous ne dites pas ce qu'ils sont.

AmbroseChapelle
la source
4

J'ai vérifié toutes les réponses sur le Web et la meilleure semblait être: (en fait, ce n'est pas le cas)

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

en premier.

Cependant, si vous ajoutez le paramètre cache = none (qui est un mot statique "aucun"), cela n'affecte rien, le navigateur se charge toujours à partir du cache.

La solution à ce problème était:

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

où vous ajoutez essentiellement un horodatage unix pour rendre le paramètre dynamique et pas de cache, cela a fonctionné.

Cependant, mon problème était un peu différent: je chargeais à la volée une image de graphique php générée à la volée et contrôlais la page avec les paramètres $ _GET. Je voulais que l'image soit lue à partir du cache lorsque le paramètre URL GET reste le même, et ne pas mettre en cache lorsque les paramètres GET changent.

Pour résoudre ce problème, j'avais besoin de hacher $ _GET mais comme il s'agit d'un tableau, voici la solution:

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

Modifier :

Bien que la solution ci-dessus fonctionne très bien, vous souhaitez parfois servir la version mise en cache JUSQU'À ce que le fichier soit modifié. (avec la solution ci-dessus, cela désactive complètement le cache pour cette image) Donc, pour servir l'image mise en cache à partir du navigateur JUSQU'À ce qu'il y ait un changement dans l'utilisation du fichier image:

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

filemtime () obtient l'heure de modification du fichier.

Tarik
la source
2
La filemtimesolution est meilleure aussi car elle md5demande beaucoup de puissance de traitement.
oriadam
2
J'ai passé des jours à essayer d'obtenir une application basée sur Chromium pour arrêter la mise en cache des images. Le? Nocache avec l'écho du temps a résolu le problème. Je vous remercie!
Woody
2

Votre problème est que malgré l'en- Expires:tête, votre navigateur réutilise sa copie en mémoire de l'image d'avant sa mise à jour, plutôt que de même vérifier son cache.

J'ai eu une situation très similaire en téléchargeant des images de produits dans le backend d'administration pour un site de type magasin, et dans mon cas, j'ai décidé que la meilleure option était d'utiliser javascript pour forcer une actualisation d'image, sans utiliser aucune des techniques de modification d'URL d'autres personnes ont déjà mentionné ici. Au lieu de cela, j'ai mis l'URL de l'image dans un IFRAME caché, appelé location.reload(true)sur la fenêtre de l'IFRAME, puis j'ai remplacé mon image sur la page. Cela force une actualisation de l'image, non seulement sur la page sur laquelle je suis, mais également sur toutes les pages ultérieures que je visite - sans que le client ou le serveur n'ait à se souvenir des paramètres de chaîne de requête d'URL ou d'identifiant de fragment.

J'ai posté un code pour le faire dans ma réponse ici .

Faire
la source
2

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

donnera toujours à votre fichier un nombre aléatoire à la fin et arrêtera sa mise en cache

BritanniqueSam
la source
Il convient de noter que la mise en œuvre de cette solution met du stress sur le serveur par requête et ne fonctionnera pas nécessairement puisque le navigateur peut mettre en cache la page php qui contient l'heure générée au moment de la requête initiale qui est mise en cache. Cela ne fonctionnerait de manière fiable que si la page avait également une chaîne de requête dynamique.
Dmitry
1

Avec le potentiel de proxies transparents qui se comportent mal entre vous et le client, le seul moyen de garantir totalement que les images ne seront pas mises en cache est de leur donner un uri unique, quelque chose comme le marquage d'un horodatage sous forme de chaîne de requête ou dans le cadre du chemin.

Si cet horodatage correspond à la dernière heure de mise à jour de l'image, vous pouvez alors mettre en cache lorsque vous en avez besoin et servir la nouvelle image au bon moment.

user7375
la source
1

Je suppose que la question originale concerne les images stockées avec des informations textuelles. Donc, si vous avez accès à un contexte de texte lors de la génération de src = ... url, envisagez de stocker / utiliser CRC32 d'octets d'image au lieu d'un horodatage ou aléatoire sans signification. Ensuite, si la page contenant beaucoup d'images s'affiche, seules les images mises à jour seront rechargées. Finalement, si le stockage CRC est impossible, il peut être calculé et ajouté à l'url au moment de l'exécution.

jbw
la source
Ou, utilisez l'heure de dernière modification de l'image, au lieu d'un CRC, encore mieux!
Fait
1

De mon point de vue, désactiver la mise en cache des images est une mauvaise idée. Du tout.

Le problème racine ici est - comment forcer le navigateur à mettre à jour l'image, lorsqu'elle a été mise à jour côté serveur.

Encore une fois, de mon point de vue personnel, la meilleure solution est de désactiver l'accès direct aux images. Au lieu de cela, accédez aux images via un filtre / servlet côté serveur / d'autres outils / services similaires.

Dans mon cas, c'est un service de repos, qui renvoie l'image et attache ETag en réponse. Le service conserve le hachage de tous les fichiers, si le fichier est modifié, le hachage est mis à jour. Cela fonctionne parfaitement dans tous les navigateurs modernes. Oui, la mise en œuvre prend du temps, mais cela en vaut la peine.

La seule exception - sont les favicons. Pour certaines raisons, cela ne fonctionne pas. Je ne pouvais pas forcer le navigateur à mettre à jour son cache du côté serveur. ETags, Cache Control, Expires, Pragma headers, rien n'y fait.

Dans ce cas, ajouter un paramètre aléatoire / version dans l'url, semble-t-il, est la seule solution.

Alexandr
la source
1

Idéalement, vous devriez ajouter un bouton / une liaison de touches / un menu à chaque page Web avec une option pour synchroniser le contenu.

Pour ce faire, vous devez garder une trace des ressources qui peuvent avoir besoin d'être synchronisées et utiliser xhr pour sonder les images avec une chaîne de requête dynamique ou créer une image au moment de l'exécution avec src en utilisant une chaîne de requête dynamique. Utilisez ensuite un mécanisme de diffusion pour notifier à tous les composants des pages Web qui utilisent la ressource la mise à jour d'utiliser la ressource avec une chaîne de requête dynamique ajoutée à son URL.

Un exemple naïf ressemble à ceci:

Normalement, l'image est affichée et mise en cache, mais si l'utilisateur appuie sur le bouton, une requête xhr est envoyée à la ressource avec une chaîne de requête de temps ajoutée; puisque le temps peut être supposé différent à chaque pression, il s'assurera que le navigateur contournera le cache car il ne peut pas dire si la ressource est générée dynamiquement côté serveur en fonction de la requête, ou s'il s'agit d'un statique ressource qui ignore la requête.

Le résultat est que vous pouvez éviter que tous vos utilisateurs vous bombardent tout le temps de demandes de ressources, mais en même temps, permettez aux utilisateurs de mettre à jour leurs ressources s'ils soupçonnent qu'ils ne sont pas synchronisés.

<!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>
Dmitry
la source
0

Vous devez utiliser un ou plusieurs noms de fichier uniques. Comme ça

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

Mais cette technique signifie une utilisation élevée du serveur et un gaspillage de bande passante. Au lieu de cela, vous devez utiliser le numéro de version ou la date. Exemple:

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

Mais vous voulez qu'il ne serve jamais d'image à partir du cache. Pour cela, si la page n'utilise pas de cache de page , c'est possible avec PHP ou côté serveur.

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

Cependant, ce n'est toujours pas efficace. Raison: Cache du navigateur ... La dernière méthode, mais la plus efficace, est le JAVASCRIPT natif. Ce code simple trouve toutes les images avec une classe "NO-CACHE" et rend les images presque uniques. Mettez ceci entre les balises de script.

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

USAGE

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

RÉSULTAT (s) comme celui-ci

https://example.com/image.png?1582018163634
Ali
la source