J'ai vu beaucoup de variations dans les réponses sur la façon de procéder, alors j'ai pensé les résumer ici (plus ajouter une 4ème méthode de ma propre invention):
(1) Ajoutez un paramètre de requête de contournement du cache unique à l'URL, tel que:
newImage.src = "image.jpg?t=" + new Date().getTime();
Avantages: 100% fiable, rapide et facile à comprendre et à mettre en œuvre.
Inconvénients: contourne complètement la mise en cache, ce qui signifie des retards inutiles et une utilisation de la bande passante chaque fois que l'image ne change pas entre les vues. Remplira potentiellement le cache du navigateur (et les caches intermédiaires) avec de très nombreuses copies d'exactement la même image! En outre, nécessite la modification de l'URL de l'image.
Quand l'utiliser: à utiliser lorsque l'image change constamment, comme pour un flux webcam en direct. Si vous utilisez cette méthode, assurez-vous de servir les images elles-mêmes avec Cache-control: no-cache
des en-têtes HTTP !!! (Souvent, cela peut être configuré à l'aide d'un fichier .htaccess). Sinon, vous remplissez progressivement les caches avec d'anciennes versions de l'image!
(2) Ajoutez un paramètre de requête à l'URL qui ne change que lorsque le fichier le fait, par exemple:
echo '<img src="image.jpg?m=' . filemtime('image.jpg') . '">';
(C'est du code PHP côté serveur, mais le point important ici est juste qu'une chaîne de requête ? M = [heure de la dernière modification du fichier] est ajoutée au nom du fichier).
Avantages: 100% fiable, rapide et facile à comprendre et à mettre en œuvre, et préserve parfaitement les avantages de la mise en cache.
Inconvénients: nécessite de modifier l'URL de l'image. En outre, un peu plus de travail pour le serveur - il doit avoir accès à l'heure de dernière modification du fichier. Exige également des informations côté serveur, donc ne convient pas à une solution purement côté client uniquement pour rechercher une image actualisée.
Quand l'utiliser: lorsque vous souhaitez mettre en cache des images, mais que vous devrez peut-être les mettre à jour à la fin du serveur de temps en temps sans changer le nom du fichier lui-même. ET lorsque vous pouvez facilement vous assurer que la chaîne de requête correcte est ajoutée à chaque instance d'image dans votre code HTML.
(3) Serve vos images avec l' en- tête Cache-control: max-age=0, must-revalidate
et ajouter un cadre unique memcache identifiant fragment -busting à l'URL, tels que:
newImage.src = "image.jpg#" + new Date().getTime();
L'idée ici est que l'en-tête de contrôle du cache place les images dans le cache du navigateur, mais les marque immédiatement périmées, de sorte que chaque fois qu'elles sont ré-affichées, le navigateur doit vérifier avec le serveur pour voir si elles ont changé. Cela garantit que le cache HTTP du navigateur renvoie toujours la dernière copie de l'image. Cependant, les navigateurs réutilisent souvent une copie en mémoire d'une image s'ils en ont une, et ne vérifient même pas leur cache HTTP dans ce cas. Pour éviter cela, un identifiant de fragment est utilisé: la comparaison des images en mémoire src
inclut l'identificateur de fragment, mais il est supprimé avant d'interroger le cache HTTP. (Ainsi, par exemple, image.jpg#A
et image.jpg#B
peuvent tous deux être affichés à partir de l' image.jpg
entrée dans le cache HTTP du navigateur, maisimage.jpg#B
ne serait jamais affiché en utilisant les données d'image conservées en mémoire depuis la image.jpg#A
dernière fois).
Avantages: utilise correctement les mécanismes de mise en cache HTTP et utilise les images mises en cache si elles n'ont pas changé. Fonctionne pour les serveurs qui s'étouffent avec une chaîne de requête ajoutée à une URL d'image statique (puisque les serveurs ne voient jamais les identificateurs de fragments - ils sont réservés à l'usage exclusif des navigateurs).
Inconvénients: repose sur le comportement quelque peu douteux (ou du moins mal documenté) des navigateurs, en ce qui concerne les images avec des identificateurs de fragment dans leurs URL (Cependant, j'ai testé cela avec succès dans FF27, Chrome33 et IE11). Envoie toujours une demande de revalidation au serveur pour chaque vue d'image, ce qui peut être excessif si les images ne changent que rarement et / ou la latence est un gros problème (car vous devez attendre la réponse de revalidation même lorsque l'image mise en cache est toujours bonne) . Nécessite de modifier les URL des images.
Quand l'utiliser: à utiliser lorsque les images peuvent changer fréquemment ou doivent être actualisées par intermittence par le client sans implication de script côté serveur, mais là où vous voulez toujours l'avantage de la mise en cache. Par exemple, interroger une webcam en direct qui met à jour une image de manière irrégulière toutes les quelques minutes. Vous pouvez également utiliser au lieu de (1) ou (2) si votre serveur n'autorise pas les chaînes de requête sur les URL d'images statiques.
(4) Rafraîchissez de force une image particulière en utilisant Javascript, en la chargeant d'abord dans un fichier caché <iframe>
puis en appelant location.reload(true)
les iframes contentWindow
.
Les étapes sont les suivantes:
Chargez l'image à rafraîchir dans une iframe cachée. Il ne s'agit que d'une étape de configuration - elle peut être effectuée longtemps à l'avance pour l'actualisation réelle, si vous le souhaitez. Peu importe que l'image ne se charge pas à ce stade!
Une fois cela fait, videz toutes les copies de cette image sur vos pages ou n'importe où dans n'importe quel nœud DOM (même ceux hors page stockés dans des variables javascript). Cela est nécessaire car le navigateur peut sinon afficher l'image à partir d'une copie en mémoire périmée (IE11 en particulier): vous devez vous assurer que toutes les copies en mémoire sont effacées, avant d'actualiser le cache HTTP. Si un autre code javascript s'exécute de manière asynchrone, vous devrez peut-être également empêcher ce code de créer de nouvelles copies de l'image à actualiser entre-temps.
Appelle iframe.contentWindow.location.reload(true)
. Cela true
force un contournement du cache, en rechargeant directement depuis le serveur et en remplaçant la copie en cache existante.
Une fois le rechargement terminé, restaurez les images masquées. Ils devraient maintenant afficher la nouvelle version du serveur!
Pour les images du même domaine, vous pouvez charger l'image directement dans l'iframe. Pour les images interdomaines, vous devez à la place charger une page HTML à partir de votre domaine qui contient l'image dans une <img>
balise, sinon vous obtiendrez une erreur «Accès refusé» lors de la tentative d'appel iframe.contentWindow.reload(...)
.
Avantages: Fonctionne exactement comme la fonction image.reload () que vous souhaitez que le DOM ait! Permet aux images d'être mises en cache normalement (même avec des dates d'expiration futures si vous le souhaitez, évitant ainsi une revalidation fréquente). Vous permet d'actualiser une image particulière sans modifier les URL de cette image sur la page actuelle ou sur toute autre page, en utilisant uniquement du code côté client.
Inconvénients: repose sur Javascript. Il n'est pas garanti à 100% de fonctionner correctement dans tous les navigateurs (j'ai testé cela avec succès dans FF27, Chrome33 et IE11). Très compliqué par rapport aux autres méthodes.
Quand l'utiliser: lorsque vous avez une collection d'images essentiellement statiques que vous souhaitez mettre en cache, mais vous devez toujours pouvoir les mettre à jour de temps en temps et obtenir un retour visuel immédiat que la mise à jour a eu lieu. (Surtout lorsque le rafraîchissement de la page entière du navigateur ne fonctionnerait pas, comme dans certaines applications Web construites sur AJAX par exemple). Et lorsque les méthodes (1) - (3) ne sont pas réalisables car (pour une raison quelconque), vous ne pouvez pas modifier toutes les URL susceptibles d'afficher l'image que vous devez mettre à jour. (Notez que l'utilisation de ces 3 méthodes l'image sera actualisée, mais si une autre page essaie ensuite d'afficher cette image sans la chaîne de requête ou l'identificateur de fragment approprié, elle peut afficher une version plus ancienne à la place).
Les détails de la mise en œuvre d'une manière fée robuste et flexible sont donnés ci-dessous:
Supposons que votre site contient un pixel blanc 1x1 .gif sur le chemin d'URL /img/1x1blank.gif
, et a également le script PHP d' une ligne suivante (uniquement pour l' application de rafraîchissement forcé de interdomaines images, et peuvent être réécrites dans un langage de script côté serveur , bien sûr) sur le chemin URL /echoimg.php
:
<img src="<?=htmlspecialchars(@$_GET['src'],ENT_COMPAT|ENT_HTML5,'UTF-8')?>">
Ensuite, voici une implémentation réaliste de la façon dont vous pourriez faire tout cela en Javascript. Cela semble un peu compliqué, mais il y a beaucoup de commentaires, et la fonction importante est juste forceImgReload () - les deux premières images juste vides et non vides, et devraient être conçues pour fonctionner efficacement avec votre propre HTML, alors codez-les comme fonctionne mieux pour vous; une grande partie des complications peuvent être inutiles pour votre site Web:
// This function should blank all images that have a matching src, by changing their src property to /img/1x1blank.gif.
// ##### You should code the actual contents of this function according to your page design, and what images there are on them!!! #####
// Optionally it may return an array (or other collection or data structure) of those images affected.
// This can be used by imgReloadRestore() to restore them later, if that's an efficient way of doing it (otherwise, you don't need to return anything).
// NOTE that the src argument here is just passed on from forceImgReload(), and MAY be a relative URI;
// However, be aware that if you're reading the src property of an <img> DOM object, you'll always get back a fully-qualified URI,
// even if the src attribute was a relative one in the original HTML. So watch out if trying to compare the two!
// NOTE that if your page design makes it more efficient to obtain (say) an image id or list of ids (of identical images) *first*, and only then get the image src,
// you can pass this id or list data to forceImgReload() along with (or instead of) a src argument: just add an extra or replacement parameter for this information to
// this function, to imgReloadRestore(), to forceImgReload(), and to the anonymous function returned by forceImgReload() (and make it overwrite the earlier parameter variable from forceImgReload() if truthy), as appropriate.
function imgReloadBlank(src)
{
// ##### Everything here is provisional on the way the pages are designed, and what images they contain; what follows is for example purposes only!
// ##### For really simple pages containing just a single image that's always the one being refreshed, this function could be as simple as just the one line:
// ##### document.getElementById("myImage").src = "/img/1x1blank.gif";
var blankList = [],
fullSrc = /* Fully qualified (absolute) src - i.e. prepend protocol, server/domain, and path if not present in src */,
imgs, img, i;
for each (/* window accessible from this one, i.e. this window, and child frames/iframes, the parent window, anything opened via window.open(), and anything recursively reachable from there */)
{
// get list of matching images:
imgs = theWindow.document.body.getElementsByTagName("img");
for (i = imgs.length; i--;) if ((img = imgs[i]).src===fullSrc) // could instead use body.querySelectorAll(), to check both tag name and src attribute, which would probably be more efficient, where supported
{
img.src = "/img/1x1blank.gif"; // blank them
blankList.push(img); // optionally, save list of blanked images to make restoring easy later on
}
}
for each (/* img DOM node held only by javascript, for example in any image-caching script */) if (img.src===fullSrc)
{
img.src = "/img/1x1blank.gif"; // do the same as for on-page images!
blankList.push(img);
}
// ##### If necessary, do something here that tells all accessible windows not to create any *new* images with src===fullSrc, until further notice,
// ##### (or perhaps to create them initially blank instead and add them to blankList).
// ##### For example, you might have (say) a global object window.top.blankedSrces as a propery of your topmost window, initially set = {}. Then you could do:
// #####
// ##### var bs = window.top.blankedSrces;
// ##### if (bs.hasOwnProperty(src)) bs[src]++; else bs[src] = 1;
// #####
// ##### And before creating a new image using javascript, you'd first ensure that (blankedSrces.hasOwnProperty(src)) was false...
// ##### Note that incrementing a counter here rather than just setting a flag allows for the possibility that multiple forced-reloads of the same image are underway at once, or are overlapping.
return blankList; // optional - only if using blankList for restoring back the blanked images! This just gets passed in to imgReloadRestore(), it isn't used otherwise.
}
// This function restores all blanked images, that were blanked out by imgReloadBlank(src) for the matching src argument.
// ##### You should code the actual contents of this function according to your page design, and what images there are on them, as well as how/if images are dimensioned, etc!!! #####
function imgReloadRestore(src,blankList,imgDim,loadError);
{
// ##### Everything here is provisional on the way the pages are designed, and what images they contain; what follows is for example purposes only!
// ##### For really simple pages containing just a single image that's always the one being refreshed, this function could be as simple as just the one line:
// ##### document.getElementById("myImage").src = src;
// ##### if in imgReloadBlank() you did something to tell all accessible windows not to create any *new* images with src===fullSrc until further notice, retract that setting now!
// ##### For example, if you used the global object window.top.blankedSrces as described there, then you could do:
// #####
// ##### var bs = window.top.blankedSrces;
// ##### if (bs.hasOwnProperty(src)&&--bs[src]) return; else delete bs[src]; // return here means don't restore until ALL forced reloads complete.
var i, img, width = imgDim&&imgDim[0], height = imgDim&&imgDim[1];
if (width) width += "px";
if (height) height += "px";
if (loadError) {/* If you want, do something about an image that couldn't load, e.g: src = "/img/brokenImg.jpg"; or alert("Couldn't refresh image from server!"); */}
// If you saved & returned blankList in imgReloadBlank(), you can just use this to restore:
for (i = blankList.length; i--;)
{
(img = blankList[i]).src = src;
if (width) img.style.width = width;
if (height) img.style.height = height;
}
}
// Force an image to be reloaded from the server, bypassing/refreshing the cache.
// due to limitations of the browser API, this actually requires TWO load attempts - an initial load into a hidden iframe, and then a call to iframe.contentWindow.location.reload(true);
// If image is from a different domain (i.e. cross-domain restrictions are in effect, you must set isCrossDomain = true, or the script will crash!
// imgDim is a 2-element array containing the image x and y dimensions, or it may be omitted or null; it can be used to set a new image size at the same time the image is updated, if applicable.
// if "twostage" is true, the first load will occur immediately, and the return value will be a function
// that takes a boolean parameter (true to proceed with the 2nd load (including the blank-and-reload procedure), false to cancel) and an optional updated imgDim.
// This allows you to do the first load early... for example during an upload (to the server) of the image you want to (then) refresh.
function forceImgReload(src, isCrossDomain, imgDim, twostage)
{
var blankList, step = 0, // step: 0 - started initial load, 1 - wait before proceeding (twostage mode only), 2 - started forced reload, 3 - cancelled
iframe = window.document.createElement("iframe"), // Hidden iframe, in which to perform the load+reload.
loadCallback = function(e) // Callback function, called after iframe load+reload completes (or fails).
{ // Will be called TWICE unless twostage-mode process is cancelled. (Once after load, once after reload).
if (!step) // initial load just completed. Note that it doesn't actually matter if this load succeeded or not!
{
if (twostage) step = 1; // wait for twostage-mode proceed or cancel; don't do anything else just yet
else { step = 2; blankList = imgReloadBlank(src); iframe.contentWindow.location.reload(true); } // initiate forced-reload
}
else if (step===2) // forced re-load is done
{
imgReloadRestore(src,blankList,imgDim,(e||window.event).type==="error"); // last parameter checks whether loadCallback was called from the "load" or the "error" event.
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
}
}
iframe.style.display = "none";
window.parent.document.body.appendChild(iframe); // NOTE: if this is done AFTER setting src, Firefox MAY fail to fire the load event!
iframe.addEventListener("load",loadCallback,false);
iframe.addEventListener("error",loadCallback,false);
iframe.src = (isCrossDomain ? "/echoimg.php?src="+encodeURIComponent(src) : src); // If src is cross-domain, script will crash unless we embed the image in a same-domain html page (using server-side script)!!!
return (twostage
? function(proceed,dim)
{
if (!twostage) return;
twostage = false;
if (proceed)
{
imgDim = (dim||imgDim); // overwrite imgDim passed in to forceImgReload() - just in case you know the correct img dimensions now, but didn't when forceImgReload() was called.
if (step===1) { step = 2; blankList = imgReloadBlank(src); iframe.contentWindow.location.reload(true); }
}
else
{
step = 3;
if (iframe.contentWindow.stop) iframe.contentWindow.stop();
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
}
}
: null);
}
Ensuite, pour forcer un rafraîchissement d'une image située sur le même domaine que votre page, vous pouvez simplement faire:
forceImgReload("myimage.jpg");
Pour rafraîchir une image ailleurs (interdomaine):
forceImgReload("http://someother.server.com/someimage.jpg", true);
Une application plus avancée pourrait être de recharger une image après avoir téléchargé une nouvelle version sur votre serveur, en préparant la phase initiale du processus de rechargement simultanément avec le téléchargement, afin de minimiser le délai de rechargement visible pour l'utilisateur. Si vous effectuez le téléchargement via AJAX et que le serveur renvoie un tableau JSON très simple [succès, largeur, hauteur], votre code pourrait ressembler à ceci:
// fileForm is a reference to the form that has a the <input typ="file"> on it, for uploading.
// serverURL is the url at which the uploaded image will be accessible from, once uploaded.
// The response from uploadImageToServer.php is a JSON array [success, width, height]. (A boolean and two ints).
function uploadAndRefreshCache(fileForm, serverURL)
{
var xhr = new XMLHttpRequest(),
proceedWithImageRefresh = forceImgReload(serverURL, false, null, true);
xhr.addEventListener("load", function(){ var arr = JSON.parse(xhr.responseText); if (!(arr&&arr[0])) { proceedWithImageRefresh(false); doSomethingOnUploadFailure(...); } else { proceedWithImageRefresh(true,[arr[1],ar[2]]); doSomethingOnUploadSuccess(...); }});
xhr.addEventListener("error", function(){ proceedWithImageRefresh(false); doSomethingOnUploadError(...); });
xhr.addEventListener("abort", function(){ proceedWithImageRefresh(false); doSomethingOnUploadAborted(...); });
// add additional event listener(s) to track upload progress for graphical progress bar, etc...
xhr.open("post","uploadImageToServer.php");
xhr.send(new FormData(fileForm));
}
Une dernière note: bien que cette rubrique concerne les images, elle s'applique potentiellement à d'autres types de fichiers ou de ressources. Par exemple, empêcher l'utilisation de scripts obsolètes ou de fichiers css, ou peut-être même actualiser des documents PDF mis à jour (en utilisant (4) uniquement s'il est configuré pour s'ouvrir dans le navigateur). La méthode (4) peut nécessiter certaines modifications du javascript ci-dessus, dans ces cas.