web-dev-qa-db-fra.com

Comment forcer un navigateur Web à ne PAS mettre en cache des images

Contexte

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

Le problème

Le problème survient lorsque l'administrateur souhaite modifier l'image. Il lui suffirait d'appuyer sur le bouton "Parcourir", de choisir une nouvelle photo et d'appuyer sur OK. Et ça marche bien.

Une fois l'image téléchargée, mon CGI d’arrière-plan gère le téléchargement et recharge correctement le formulaire.

Le problème est que l'image affichée ne le fait pas est actualisée. 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 IS CACHED dans le navigateur Web. Si l'administrateur appuie sur le bouton RELOAD dans Firefox/Explorer/Safari, tout est rafraîchi correctement et la nouvelle image uniquement apparaît.

Ma solution - ne fonctionne pas

J'essaie de contrôler le cache en écrivant une instruction HTTP Expires dont la date est très éloignée.

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

Rappelez-vous que je suis du côté administratif et que le chargement des pages prend un peu plus de temps, peu m'importe, car elles sont toujours périmé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é en Image.jpg (pour simplifier les choses lors de son utilisation). Lors du remplacement de l'image existante par une nouvelle image, 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/fournisseur de services Internet. Il utilise Apache.

Question

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

Je jongle avec l'option de réellement "enregistrer 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 sera également modifié. 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. En outre, cela ne fonctionnera toujours pas si la nouvelle image téléchargée a le même nom (disons que l'image est un peu reportée et qu'elle est re-téléchargée).

113
Philibert Perusse

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 en Javascript avec performance.now() ou en Python avec time.time()

171
epochwolf

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

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

Que dit le RFC HTTP:

Cache-Control: no-cache

Mais ça ne marche pas très bien :)

45
Armin Ronacher

J'utilise fonction de modification du 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, car son horodatage modifié est différent.

18
Rick

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 simplifier les choses lors de son utilisation). Lors du remplacement de l'image existante par une nouvelle image, le nom ne change pas non plus. Seul le contenu du fichier image change.

Je pense qu’il existe 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 on se retrouve après avoir réfléchi (car elles sont faciles à 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 php est si populaire;)

15
x-yuri

Vous pouvez écrire un script proxy pour le service 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, soit des en-têtes sans cache, soit un nombre aléatoire dans image src devraient suffire, mais nous voulons être à l’épreuve des balles.

7
Mindeh

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

Vous ne savez pas ce qui fonctionne sur le 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: Ne fonctionne que lorsque la page Web est actualisée (même après une visite ultérieure). SAFARI et iPad: ça ne marche pas, je dois vider l'historique et les données Web.

Des idées sur SAFARI/iPad?

6
Timmy T.

use Class = "NO-CACHE"

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

6
Aref Rostamkhani

J'ai vérifié toutes les réponses sur le Web et le meilleur 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 "none"), cela n'a aucune incidence, le navigateur se charge toujours à partir du cache.

La solution à ce problème était la suivante:

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

où vous avez essentiellement ajouté un horodatage Unix pour rendre le paramètre dynamique et aucun cache, cela a fonctionné.

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

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

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

Edit:

Bien que la solution ci-dessus fonctionne correctement, vous souhaitez parfois servir la version mise en cache jusqu'à ce que le fichier soit modifié. (avec la solution ci-dessus, le cache est complètement désactivé pour cette image). Ainsi, pour servir une image en cache à partir du navigateur jusqu'à la modification, l'utilisation du fichier image a changé:

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

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

4
Tarik

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 simplifier les choses lors de son utilisation).

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

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

4
AmbroseChapel

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

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

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

2
Doin

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

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

2
BritishSam

Je suppose que la question originale concerne les images stockées avec des informations textuelles. Ainsi, si vous avez accès à un contexte de texte lors de la génération de src = ... url, envisagez de stocker/utiliser des octets d'image CRC32 au lieu d'un tampon aléatoire ou d'un horodatage. Ensuite, si la page contenant de nombreuses images est affichée, 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.

1
jbw

Avec le potentiel de proxys transparents mal agencés entre vous et le client, le seul moyen de garantir totalement que les images ne seront pas mises en cache est de leur attribuer un uri unique, comme par exemple en affichant un horodatage sous forme de chaîne de requête ou dans le cadre du fichier. 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.

1
user7375

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

Le problème fondamental est le suivant: comment forcer le navigateur à mettre à jour une image lorsqu'elle a été mise à jour côté serveur.

Encore une fois, de mon point de vue personnel, la meilleure solution consiste à désactiver l’accès direct aux images. Accédez plutôt aux images via un filtre/servlet/autre outil/service similaire côté serveur.

Dans mon cas, il s’agit d’un service de repos, qui renvoie une 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, sa mise en œuvre prend du temps, mais cela en vaut la peine.

La seule exception: 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, en-têtes Pragma, rien n'y fait.

Dans ce cas, il semble que l'ajout d'un paramètre random/version à l'URL est la seule solution.

1
Alexandr

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

Pour ce faire, gardez une trace des ressources pouvant nécessiter une synchronisation et utilisez soit xhr pour sonder les images avec une chaîne de requête dynamique, soit créez une image au moment de l'exécution avec src à l'aide d'une chaîne de requête dynamique. Utilisez ensuite un mécanisme de diffusion pour notifier tous les composants des pages Web utilisant la ressource à mettre à jour pour 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 a appuyé sur le bouton, une demande xhr est envoyée à la ressource avec une chaîne de requête temporelle qui lui est ajoutée. étant donné que le temps peut être supposé différent sur chaque presse, 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'une requête statique. ressource qui ignore la requête.

Le résultat est que vous pouvez éviter que tous vos utilisateurs vous bombardent de demandes de ressources tout le temps, tout en permettant aux utilisateurs de disposer d'un mécanisme pour 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>
0
Dmitry