Actualiser l'image avec une nouvelle à la même URL

333

J'accède à un lien sur mon site qui fournira une nouvelle image à chaque accès.

Le problème que je rencontre est que si j'essaie de charger l'image en arrière-plan puis de mettre à jour celle sur la page, l'image ne change pas - bien qu'elle soit mise à jour lorsque je recharge la page.

var newImage = new Image();
newImage.src = "http://localhost/image.jpg";

function updateImage()
{
if(newImage.complete) {
    document.getElementById("theText").src = newImage.src;
    newImage = new Image();
    number++;
    newImage.src = "http://localhost/image/id/image.jpg?time=" + new Date();
}

    setTimeout(updateImage, 1000);
}

En-têtes tels que FireFox les voit:

HTTP/1.x 200 OK
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Transfer-Encoding: chunked
Content-Type: image/jpeg
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Server: Microsoft-HTTPAPI/1.0
Date: Thu, 02 Jul 2009 23:06:04 GMT

J'ai besoin de forcer un rafraîchissement de cette image uniquement sur la page. Des idées?

QueueHammer
la source

Réponses:

351

Essayez d'ajouter un brise-cache à la fin de l'URL:

newImage.src = "http://localhost/image.jpg?" + new Date().getTime();

Cela ajoutera automatiquement l'horodatage actuel lorsque vous créez l'image, et cela fera que le navigateur recherchera à nouveau l'image au lieu de récupérer celle du cache.

Paolo Bergantino
la source
26
Ce n'est pas une très bonne solution car elle submergera les caches (à la fois locales et en amont). La réponse d'Aya a une meilleure façon de gérer cela.
Tgr
1
De plus, le réaffichage de la même image ailleurs, sans le "disjoncteur de cache" plus tard, montre toujours l'ancienne version mise en cache (au moins dans firefox) à la fois? et # :(
T4NK3R
3
Vous pouvez faire encore moins de code avec ceci:'image.jpg?' + (+new Date())
Lev Lukomsky
4
Il y a Date.now()pour cela
vp_arth
2
Pourquoi pasMath.random()
Gowtham Gopalakrishnan
227

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-cachedes 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-revalidateet 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 srcinclut l'identificateur de fragment, mais il est supprimé avant d'interroger le cache HTTP. (Ainsi, par exemple, image.jpg#Aet image.jpg#Bpeuvent tous deux être affichés à partir de l' image.jpgentrée dans le cache HTTP du navigateur, maisimage.jpg#Bne serait jamais affiché en utilisant les données d'image conservées en mémoire depuis la image.jpg#Aderniè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 trueforce 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.

Doin
la source
J'aime l'idée de la méthode 4, mais vous ne pouvez pas charger de contenu externe avec un Iframe, n'est-ce pas? J'utilise actuellement la méthode 3 dans une application Web à page unique, mais je n'aime pas le fait que vous devez recharger la page entière pour obtenir la nouvelle image, même si le HTML du modèle est rechargé.
Emilios1995
@Emilios: ... De plus, je ne comprends pas votre commentaire sur le fait de devoir recharger toute la page. Les deux méthodes (3) et (4) peuvent être implémentées en javascript côté client, sans recharger quoi que ce soit, sauf l'image que vous actualisez. Pour la méthode (3), cela signifierait simplement utiliser javascript pour changer la propriété 'src' de votre image (par exemple) image.jpg#123en image.jpg#124(ou autre, tant que le bit après le '#' change). Pourriez-vous clarifier ce que vous rechargez et pourquoi?
Doin
@Emilios: Vous pouvez en effet charger du contenu externe (inter-domaines) dans un iframe ... mais vous ne pouvez pas ensuite y accéder contentWindowvia javascript pour faire l' reload(true)appel qui est la partie critique de la méthode ... donc non, méthode (4 ) ne fonctionnera pas pour le contenu interdomaine. Bien repéré; Je mettrai à jour les "inconvénients" pour l'inclure.
Doin
@Emilios: Oups, non, je ne le ferai pas: j'ai réalisé qu'un simple correctif (désormais inclus dans ma réponse) lui permet de fonctionner également pour les images interdomaines (à condition que vous puissiez mettre un script côté serveur sur votre serveur).
Doin
@pseudosavant: Malheureusement, je ne le remarque que ~ 17 mois plus tard, mais je suis désolé de devoir vous le dire, vos modifications de mon code ont été gravement cassées. (Pour être honnête, je ne pense pas non plus que le code de rappel que j'avais initialement était correct). J'ai maintenant réécrit la partie (4) en profondeur, à la fois explication et code. Votre code précédent n'a jamais masqué les images (il pourrait donc échouer de manière étrange, en particulier dans IE et surtout si l'image a été affichée à plusieurs endroits), mais pire, il a également supprimé l'iframe immédiatement après le démarrage du rechargement complet, ce qui signifiait probablement qu'il avait travailler uniquement par intermittence ou pas du tout. Désolé!
Doin
185

Comme alternative à ...

newImage.src = "http://localhost/image.jpg?" + new Date().getTime();

...Il paraît que...

newImage.src = "http://localhost/image.jpg#" + new Date().getTime();

... est suffisant pour tromper le cache du navigateur sans contourner les caches en amont, en supposant que vous ayez renvoyé les en- Cache-Controltêtes corrects . Bien que vous puissiez utiliser ...

Cache-Control: no-cache, must-revalidate

... vous perdez les avantages des en- têtes If-Modified-Sinceou If-None-Match, donc quelque chose comme ...

Cache-Control: max-age=0, must-revalidate

... devrait empêcher le navigateur de télécharger à nouveau l'image entière si elle n'a pas réellement changé. Testé et fonctionnant sur IE, Firefox et Chrome. Malheureusement, il échoue sur Safari, sauf si vous utilisez ...

Cache-Control: no-store

... bien que cela puisse être préférable au remplissage de caches en amont avec des centaines d'images identiques, en particulier lorsqu'elles s'exécutent sur votre propre serveur. ;-)

Mettre à jour (2014-09-28): De nos jours, il semble également Cache-Control: no-storenécessaire pour Chrome.

Aya
la source
1
Génial! Après beaucoup de temps à essayer de charger mes images Web qui étaient chargées avec un retard, je viens de le résoudre en appliquant votre solution (avec '#', l'utilisation de '?' Ne fonctionne pas pour moi). Merci beaucoup!!!
user304602
18
Il y a DEUX caches impliqués ici: il y a le cache HTTP normal du navigateur et un cache en mémoire d'images qu'il a récemment affiché. Ce dernier cache en mémoire est indexé par l' srcattribut complet , donc l'ajout d'un identifiant de fragment unique garantit que l'image n'est pas simplement extraite de la mémoire. Mais les identifiants de fragment ne sont pas envoyés dans le cadre des requêtes HTTP, donc le cache HTTP normal sera utilisé normalement. C'est pourquoi cette technique fonctionne.
Doin
Il existe plusieurs cache d'en-tête. en fait je ne connais pas très bien l'anglais, pouvez-vous s'il vous plaît me dire si je dois utiliser lequel?! Je veux quelque chose qui ne cache pas seulement la photo qui est modifiée (comme un captcha), et cache d'autres choses. alors Cache-Control: max-age=0, must-revalidatec'est bon pour moi?
Shafizadeh
Ça ne marche pas pour moi. La seule chose différente dans mon cas est que j'ai une URL vers une action de contrôleur qui récupère img de db. J'avais d'autres arguments pour l'action du contrôleur, je l'ai donc ajoutée comme "...... & convert = true & t =" + new Date (). GetTime (); et "...... & convert = true #" + new Date (). getTime () ;. Y a-t-il quelque chose que je fais mal?
shaffooo
1
Pour éviter la surcharge de création d'objet et / ou d'appel de méthode, vous pouvez utiliser un entier incrémenté comme cache-buster:newImage.src = "http://localhost/image.jpg#" + i++;
laindir
7

Après avoir créé la nouvelle image, supprimez-vous l'ancienne image du DOM et la remplacez-vous par la nouvelle?

Vous pouvez récupérer de nouvelles images à chaque appel de updateImage, mais ne pas les ajouter à la page.

Il existe plusieurs façons de procéder. Quelque chose comme ça marcherait.

function updateImage()
{
    var image = document.getElementById("theText");
    if(image.complete) {
        var new_image = new Image();
        //set up the new image
        new_image.id = "theText";
        new_image.src = image.src;           
        // insert new image and remove old
        image.parentNode.insertBefore(new_image,image);
        image.parentNode.removeChild(image);
    }

    setTimeout(updateImage, 1000);
}

Après l'avoir fait fonctionner, s'il y a encore des problèmes, c'est probablement un problème de mise en cache comme les autres réponses en parlent.

BaroqueBobcat
la source
3

Une réponse consiste à ajouter de manière piratée certains paramètres de requête get comme cela a été suggéré.

Une meilleure réponse consiste à émettre quelques options supplémentaires dans votre en-tête HTTP.

Pragma: no-cache
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Cache-Control: no-cache, must-revalidate

En fournissant une date dans le passé, elle ne sera pas mise en cache par le navigateur. Cache-Controla été ajouté dans HTTP / 1.1 et la balise must-revalidate indique que les proxys ne devraient jamais servir une ancienne image même dans des circonstances atténuantes, et ce Pragma: no-cachen'est pas vraiment nécessaire pour les navigateurs / caches modernes actuels, mais peut aider avec certaines anciennes implémentations cassées.

Edward KMETT
la source
3
Cela sonnait comme si cela aurait fonctionné mais il montre toujours la même image, même avec les hacks. J'ajouterai les informations d'en-tête à la question.
QueueHammer
Je viens de remarquer que vous actualisez encore et encore la même balise img. Le navigateur détecte probablement lorsque vous allez définir le src que le src n'a pas changé, et donc ce n'est pas la peine de se rafraîchir. (Étant donné que cette vérification a lieu au niveau DOM et n'a rien à voir avec la cible). Que se passe-t-il si vous ajoutez "?" + numéro - à l'url de l'image récupérée?
Edward KMETT
3

J'avais une exigence: 1) je ne peux pas en ajouter ?var=xx à l'image 2) cela devrait fonctionner entre les domaines

J'aime vraiment l'option # 4 dans cette réponse avec une mais:

  • il a des problèmes pour travailler avec le domaine croisé de manière fiable (et il faut toucher le code du serveur).

Ma façon rapide et sale est:

  1. Créer un iframe caché
  2. Chargez-y la page actuelle (ouais toute la page)
  3. iframe.contentWindow.location.reload(true);
  4. Réinitialiser la source d'image sur elle-même

C'est ici

function RefreshCachedImage() {
    if (window.self !== window.top) return; //prevent recursion
    var $img = $("#MYIMAGE");
    var src = $img.attr("src");
    var iframe = document.createElement("iframe");
    iframe.style.display = "none";
    window.parent.document.body.appendChild(iframe);
    iframe.src = window.location.href;
    setTimeout(function () {
        iframe.contentWindow.location.reload(true);
        setTimeout(function () {
            $img.removeAttr("src").attr("src", src);
        }, 2000);
    }, 2000);
}

Ouais, je sais, setTimeout ... Vous devez changer cela en événements onload appropriés.

jazzcat
la source
3
<img src='someurl.com/someimage.ext' onload='imageRefresh(this, 1000);'>

Puis ci-dessous dans certains javascript

<script language='javascript'>
 function imageRefresh(img, timeout) {
    setTimeout(function() {
     var d = new Date;
     var http = img.src;
     if (http.indexOf("&d=") != -1) { http = http.split("&d=")[0]; } 

     img.src = http + '&d=' + d.getTime();
    }, timeout);
  }
</script>

Et donc ce que cela fait, lorsque l'image se charge, la planifie pour être rechargée en 1 seconde. J'utilise ceci sur une page avec des caméras de sécurité à domicile de types différents.

Champs J
la source
2

Ce que j'ai fini par faire, c'est que le serveur mappe toute demande d'image dans ce répertoire vers la source que j'essayais de mettre à jour. J'ai ensuite fait ajouter un chiffre à la fin du nom à mon temporisateur pour que le DOM le voit comme une nouvelle image et le charge.

Par exemple

http://localhost/image.jpg
//and
http://localhost/image01.jpg

demandera le même code de génération d'image, mais il ressemblera à des images différentes pour le navigateur.

var newImage = new Image();
newImage.src = "http://localhost/image.jpg";
var count = 0;
function updateImage()
{
    if(newImage.complete) {
        document.getElementById("theText").src = newImage.src;
        newImage = new Image();
        newImage.src = "http://localhost/image/id/image" + count++ + ".jpg";
    }
    setTimeout(updateImage, 1000);
}
QueueHammer
la source
8
Cela aurait le même problème de mise en cache de plusieurs copies de l'image que la solution de chaîne de requête (Paolo et quelques autres), et nécessite des changements de serveur.
TomG
2

function reloadImage(imageId)
{
   path = '../showImage.php?cache='; //for example
   imageObject = document.getElementById(imageId);
   imageObject.src = path + (new Date()).getTime();
}
<img src='../showImage.php' id='myimage' />

<br/>

<input type='button' onclick="reloadImage('myimage')" />

Mahmoud
la source
3
veuillez expliquer au PO comment et pourquoi cela aide au lieu de simplement coller du code
nomistic
Je ne pense pas que ce ../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit)soit un nom de fichier valide ... Au moins, c'est ce qu'il essaie de charger ...
ByteHamster
changer path='../showImage.php';pourpath='../showImage.php?';
BOOMik
2
document.getElementById("img-id").src = document.getElementById("img-id").src

définir son propre src comme src.

Hardik
la source
1

Essayez d'utiliser une chaîne de requête sans valeur pour en faire une URL unique:

function updateImage()
{
    if(newImage.complete) {
        document.getElementById("theText").src = newImage.src;
        newImage = new Image();
        number++;
        newImage.src = "http://localhost/image.jpg?" + new Date();
    }

    setTimeout(updateImage, 1000);
}
Joel
la source
Ajout du WQS au code et vérification que la demande est acceptée et que le navigateur voit la réponse comme provenant de l'adresse + WQS sans rafraîchissement de l'image.
QueueHammer
1

Fortement basé sur le code n ° 4 de Doin, l'exemple ci-dessous simplifie beaucoup ce code en l'utilisant document.writeau lieu de src le iframepour prendre en charge CORS. Se concentre également uniquement sur la destruction du cache du navigateur, et non sur le rechargement de chaque image de la page.

Ci-dessous est écrit typescriptet utilise le angular $ q bibliothèque promise, juste fyi, mais devrait être assez facile à porter en javascript vanilla. La méthode est destinée à vivre à l'intérieur d'une classe tapuscrit.

Renvoie une promesse qui sera résolue lorsque l'iframe aura terminé le rechargement. Pas lourdement testé, mais fonctionne bien pour nous.

    mmForceImgReload(src: string): ng.IPromise<void> {
        var deferred = $q.defer<void>();
        var iframe = window.document.createElement("iframe");

        var firstLoad = true;
        var loadCallback = (e) => {
            if (firstLoad) {
                firstLoad = false;
                iframe.contentWindow.location.reload(true);
            } else {
                if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
                deferred.resolve();
            }
        }
        iframe.style.display = "none";
        window.parent.document.body.appendChild(iframe);
        iframe.addEventListener("load", loadCallback, false);
        iframe.addEventListener("error", loadCallback, false);
        var doc = iframe.contentWindow.document;
        doc.open();
        doc.write('<html><head><title></title></head><body><img src="' + src + '"></body></html>');
        doc.close();
        return deferred.promise;
    }
Victor
la source
Pour vous protéger contre les bogues XSS, vous devez utiliser + encodeURI(src) +pour vous échapper correctement srcdans le iframe.
Tino
1

Le code suivant est utile pour actualiser l'image lorsqu'un bouton est cliqué.

function reloadImage(imageId) {
   imgName = 'vishnu.jpg'; //for example
   imageObject = document.getElementById(imageId);
   imageObject.src = imgName;
}

<img src='vishnu.jpg' id='myimage' />

<input type='button' onclick="reloadImage('myimage')" />
Codemaker
la source
Voté. Comme il ne s'agit que d'une copie légèrement modifiée du code de @ Mahmoud, mais en revanche, cela ne rafraîchit PAS l'image
Tino
0

J'ai résolu ce problème en renvoyant les données via une servlet.

response.setContentType("image/png");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.setDateHeader("Expires", 0);

BufferedImage img = ImageIO.read(new File(imageFileName));

ImageIO.write(img, "png", response.getOutputStream());

Ensuite, à partir de la page, vous lui donnez simplement le servlet avec quelques paramètres pour récupérer le fichier image correct.

<img src="YourServlet?imageFileName=imageNum1">
tinymothbrain
la source
0

Voici ma solution. C'est très simple. La programmation des trames pourrait être meilleure.

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">      
        <title>Image Refresh</title>
    </head>

    <body>

    <!-- Get the initial image. -->
    <img id="frame" src="frame.jpg">

    <script>        
        // Use an off-screen image to load the next frame.
        var img = new Image();

        // When it is loaded...
        img.addEventListener("load", function() {

            // Set the on-screen image to the same source. This should be instant because
            // it is already loaded.
            document.getElementById("frame").src = img.src;

            // Schedule loading the next frame.
            setTimeout(function() {
                img.src = "frame.jpg?" + (new Date).getTime();
            }, 1000/15); // 15 FPS (more or less)
        })

        // Start the loading process.
        img.src = "frame.jpg?" + (new Date).getTime();
    </script>
    </body>
</html>
Timmmm
la source
0

Pas besoin de new Date().getTime()manigances. Vous pouvez tromper le navigateur en ayant une image factice invisible et en utilisant jQuery .load (), puis en créant une nouvelle image à chaque fois:

<img src="" id="dummy", style="display:none;" />  <!-- dummy img -->
<div id="pic"></div>

<script type="text/javascript">
  var url = whatever;
  // You can repeat the following as often as you like with the same url
  $("#dummy").load(url);
  var image = new Image();
  image.src = url;
  $("#pic").html("").append(image);
</script>
Bazley
la source
0

Solution simple: ajoutez cet en-tête à la réponse:

Cache-control: no-store

Pourquoi cela fonctionne est clairement expliqué sur cette page faisant autorité: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

Il explique également pourquoi no-cachene fonctionne pas.

Les autres réponses ne fonctionnent pas car:

Caching.deleteconcerne un nouveau cache que vous pouvez créer pour un travail hors ligne, voir: https://web.dev/cache-api-quick-guide/

Les fragments utilisant un # dans l'URL ne fonctionnent pas car le # indique au navigateur de ne pas envoyer de demande au serveur.

Un cache-buster avec une partie aléatoire ajoutée à l'url fonctionne, mais remplira également le cache du navigateur. Dans mon application, je voulais télécharger une image de 5 Mo toutes les quelques secondes à partir d'une webcam. Cela ne prendra qu'une heure ou moins pour geler complètement votre PC. Je ne sais toujours pas pourquoi le cache du navigateur n'est pas limité à un maximum raisonnable, mais c'est certainement un inconvénient.

Roland
la source
0

J'ai amélioré le script d' AlexMA pour montrer ma webcam sur une page Web qui télécharge périodiquement une nouvelle image avec le même nom. J'ai eu des problèmes que l'image scintillait parfois en raison d'une image cassée ou d'une image chargée (incomplète). Pour éviter le scintillement, je vérifie la hauteur naturelle de l'image car la taille de l'image de ma webcam n'a pas changé. Ce n'est que si la hauteur de l'image chargée correspond à la hauteur de l'image d'origine que l'image complète sera affichée sur la page.

  <h3>Webcam</h3>
  <p align="center">
    <img id="webcam" title="Webcam" onload="updateImage();" src="https://www.your-domain.com/webcam/current.jpg" alt="webcam image" width="900" border="0" />

    <script type="text/javascript" language="JavaScript">

    // off-screen image to preload next image
    var newImage = new Image();
    newImage.src = "https://www.your-domain.com/webcam/current.jpg";

    // remember the image height to prevent showing broken images
    var height = newImage.naturalHeight;

    function updateImage()
    {
        // for sure if the first image was a broken image
        if(newImage.naturalHeight > height)
        {
          height = newImage.naturalHeight;
        }

        // off-screen image loaded and the image was not broken
        if(newImage.complete && newImage.naturalHeight == height) 
        {
          // show the preloaded image on page
          document.getElementById("webcam").src = newImage.src;
        }

        // preload next image with cachebreaker
        newImage.src = "https://www.your-domain.com/webcam/current.jpg?time=" + new Date().getTime();

        // refresh image (set the refresh interval to half of webcam refresh, 
        // in my case the webcam refreshes every 5 seconds)
        setTimeout(updateImage, 2500);
    }

    </script>
</p>
std8590
la source
-3

J'ai utilisé le concept ci-dessous de lier d'abord l'image avec une fausse URL (tampon) et de la lier ensuite avec l'URL valide.

imgcover.ImageUrl = ConfigurationManager.AppSettings["profileLargeImgPath"] + "Myapp_CoverPic_" + userid + "Buffer.jpg";

imgcover.ImageUrl = ConfigurationManager.AppSettings["profileLargeImgPath"] + "Myapp_CoverPic_" + userid + ".jpg";

De cette façon, je force le navigateur à se rafraîchir avec une URL valide.

Prashanth Shivasubramani
la source