Actualizar imagen con una nueva en la misma url

333

Estoy accediendo a un enlace en mi sitio que proporcionará una nueva imagen cada vez que se accede.

El problema con el que me encuentro es que si trato de cargar la imagen en segundo plano y luego actualizo la de la página, la imagen no cambia, aunque se actualiza cuando vuelvo a cargar la página.

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

Encabezados como FireFox los ve:

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

Necesito forzar una actualización de solo esa imagen en la página. ¿Algunas ideas?

QueueHammer
fuente

Respuestas:

351

Intente agregar un rompedor de caché al final de la url:

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

Esto agregará la marca de tiempo actual automáticamente cuando esté creando la imagen, y hará que el navegador busque nuevamente la imagen en lugar de recuperar la que está en el caché.

Paolo Bergantino
fuente
26
No es una muy buena solución, ya que hundirá los cachés (tanto locales como aguas arriba). La respuesta de Aya tiene una mejor manera de lidiar con esto.
Tgr
1
Además, volver a mostrar la misma imagen en otro lugar, sin el "interruptor de caché" más adelante, ¿todavía muestra la versión antigua en caché (al menos en Firefox)? y # :(
T4NK3R
3
Puede hacer aún menos código con esto:'image.jpg?' + (+new Date())
Lev Lukomsky
44
Hay Date.now()para esto
vp_arth
2
¿Por qué no?Math.random()
Gowtham Gopalakrishnan
227

He visto muchas variaciones en las respuestas sobre cómo hacer esto, así que pensé en resumirlas aquí (además de agregar un cuarto método de mi propia invención):


(1) Agregue un parámetro de consulta único que revienta la caché a la URL, como:

newImage.src = "image.jpg?t=" + new Date().getTime();

Pros: 100% confiable, rápido y fácil de entender e implementar.

Contras: omite el almacenamiento en caché por completo, lo que significa retrasos innecesarios y el uso de ancho de banda siempre que la imagen no cambie entre las vistas. ¡Potencialmente llenará el caché del navegador (y cualquier caché intermedio) con muchas copias de exactamente la misma imagen! Además, requiere modificar la URL de la imagen.

Cuándo usar: se usa cuando la imagen cambia constantemente, como en el caso de una transmisión de cámara web en vivo. Si utiliza este método, asegúrese de servir las imágenes con Cache-control: no-cacheencabezados HTTP. (A menudo, esto se puede configurar usando un archivo .htaccess). ¡De lo contrario, irás llenando cachés progresivamente con versiones antiguas de la imagen!


(2) Agregue el parámetro de consulta a la URL que cambia solo cuando el archivo lo hace, por ejemplo:

echo '<img src="image.jpg?m=' . filemtime('image.jpg') . '">';

(Ese es el código del lado del servidor PHP, pero el punto importante aquí es solo que una cadena de consulta ? M = [tiempo de última modificación del archivo] se agrega al nombre del archivo).

Pros: 100% confiable, rápido y fácil de entender e implementar, y conserva perfectamente las ventajas de almacenamiento en caché.

Contras: Requiere modificar la URL de la imagen. Además, un poco más de trabajo para el servidor: tiene que obtener acceso a la hora de última modificación del archivo. Además, requiere información del lado del servidor, por lo que no es adecuado para una solución exclusivamente del lado del cliente para buscar una imagen actualizada.

Cuándo usarlo: cuando desea almacenar en caché las imágenes, pero es posible que deba actualizarlas al final del servidor de vez en cuando sin cambiar el nombre del archivo. Y cuando puede asegurarse fácilmente de que se agrega la cadena de consulta correcta a cada instancia de imagen en su HTML.


(3) Sirva sus imágenes con el encabezado Cache-control: max-age=0, must-revalidatey agregue un identificador único de fragmento que revienta la memoria caché a la URL, como:

newImage.src = "image.jpg#" + new Date().getTime();

La idea aquí es que el encabezado de control de caché coloca las imágenes en la memoria caché del navegador, pero inmediatamente las marca como obsoletas, de modo que y cada vez que se vuelven a mostrar, el navegador debe verificar con el servidor para ver si han cambiado. Esto garantiza que la memoria caché HTTP del navegador siempre devuelva la última copia de la imagen. Sin embargo, los navegadores a menudo reutilizarán una copia en memoria de una imagen si tienen una, y ni siquiera verificarán su caché HTTP en ese caso. Para evitar esto, se utiliza un identificador de fragmento: la comparación de las imágenes en memoria srcincluye el identificador de fragmento, pero se elimina antes de consultar el caché HTTP. (Entonces, por ejemplo, image.jpg#Ay image.jpg#Bambos pueden mostrarse desde la image.jpgentrada en el caché HTTP del navegador, peroimage.jpg#Bnunca se mostraría utilizando datos de imagen retenidos en la memoria de la image.jpg#Aúltima vez que se mostró).

Pros: hace un uso adecuado de los mecanismos de almacenamiento en caché HTTP y utiliza imágenes almacenadas en caché si no han cambiado. Funciona para servidores que se ahogan en una cadena de consulta agregada a una URL de imagen estática (ya que los servidores nunca ven identificadores de fragmentos, son para uso exclusivo de los navegadores).

Contras: se basa en el comportamiento algo dudoso (o al menos mal documentado) de los navegadores, con respecto a las imágenes con identificadores de fragmentos en sus URL (sin embargo, lo he probado con éxito en FF27, Chrome33 e IE11). Todavía envía una solicitud de revalidación al servidor para cada vista de imagen, lo que puede ser excesivo si las imágenes solo cambian raramente y / o la latencia es un gran problema (ya que debe esperar la respuesta de revalidación incluso cuando la imagen en caché aún es buena) . Requiere modificar las URL de las imágenes.

Cuándo usarlo: use cuándo las imágenes pueden cambiar con frecuencia, o si el cliente necesita actualizarlas de manera intermitente sin la participación del script del lado del servidor, pero donde aún desea la ventaja del almacenamiento en caché. Por ejemplo, sondear una cámara web en vivo que actualiza una imagen de forma irregular cada pocos minutos. Alternativamente, use en lugar de (1) o (2) si su servidor no permite cadenas de consulta en URL de imágenes estáticas.


(4) Actualice por la fuerza una imagen en particular usando Javascript, primero cargándola en un oculto <iframe>y luego llamando location.reload(true)al iframe contentWindow.

Los pasos son:

  • Cargue la imagen para actualizarla en un iframe oculto. Este es solo un paso de configuración: si lo desea, puede realizar la actualización real con mucha anticipación. ¡Ni siquiera importa si la imagen no se carga en esta etapa!

  • Una vez hecho esto, deje en blanco todas las copias de esa imagen en su (s) página (s) o en cualquier lugar en cualquier nodo DOM (incluso los que están fuera de la página almacenados en variables de JavaScript). Esto es necesario porque el navegador puede mostrar la imagen de una copia en memoria obsoleta (IE11 hace esto especialmente): debe asegurarse de que se borran todas las copias en memoria antes de actualizar la caché HTTP. Si otro código de JavaScript se ejecuta de forma asincrónica, es posible que también deba evitar que ese código cree nuevas copias de la imagen que debe actualizarse mientras tanto.

  • Llamada iframe.contentWindow.location.reload(true). Las truefuerzas de un bypass caché, recarga directamente desde el servidor y sobrescribir la copia en caché existente.

  • Una vez que haya terminado de volver a cargar, restaure las imágenes en blanco. ¡Ahora deberían mostrar la versión nueva del servidor!

Para imágenes del mismo dominio, puede cargar la imagen en el iframe directamente. Para las imágenes entre dominios, debe cargar una página HTML de su dominio que contenga la imagen en una <img>etiqueta; de lo contrario, recibirá un error de "Acceso denegado" al intentar llamar iframe.contentWindow.reload(...).

Pros: ¡ Funciona igual que la función image.reload () que desearía que tuviera el DOM! Permite que las imágenes se almacenen en caché normalmente (incluso con fechas de vencimiento futuras si así lo desea, evitando así la revalidación frecuente). Le permite actualizar una imagen en particular sin alterar las URL de esa imagen en la página actual o en cualquier otra página, utilizando solo el código del lado del cliente.

Contras: se basa en Javascript. No se garantiza al 100% que funcione correctamente en todos los navegadores (aunque lo he probado con éxito en FF27, Chrome33 e IE11). Muy complicado en relación con los otros métodos.

Cuándo usarlo: cuando tiene una colección de imágenes básicamente estáticas que le gustaría almacenar en caché, pero aún necesita poder actualizarlas ocasionalmente y obtener comentarios visuales inmediatos de que la actualización se realizó. (Especialmente cuando la actualización de toda la página del navegador no funcionaría, como en algunas aplicaciones web creadas en AJAX, por ejemplo). Y cuando los métodos (1) - (3) no son factibles porque (por cualquier razón) no puede cambiar todas las URL que podrían mostrar la imagen que necesita actualizar. (Tenga en cuenta que al usar esos 3 métodos, la imagen se actualizará, pero si otra página intenta mostrar esa imagen sin la cadena de consulta adecuada o el identificador de fragmento, puede mostrar una versión anterior).

Los detalles de implementar esto de una manera robusta y flexible se dan a continuación:

Supongamos que su sitio web contiene un .gif en blanco de 1x1 píxeles en la ruta URL /img/1x1blank.gif, y también tiene el siguiente script PHP de una línea (solo se requiere para aplicar la actualización forzada a las imágenes entre dominios , y se puede reescribir en cualquier lenguaje de script del lado del servidor , por supuesto) en la ruta URL /echoimg.php:

<img src="<?=htmlspecialchars(@$_GET['src'],ENT_COMPAT|ENT_HTML5,'UTF-8')?>">

Entonces, aquí hay una implementación realista de cómo podría hacer todo esto en Javascript. Parece un poco complicado, pero hay muchos comentarios, y la función importante es forceImgReload (): las dos primeras imágenes en blanco y en blanco, y deben estar diseñadas para funcionar de manera eficiente con su propio HTML, así que codifíquelas como funciona mejor para ti; Muchas de las complicaciones en ellos pueden ser innecesarias para su sitio 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);
}

Luego, para forzar una actualización de una imagen ubicada en el mismo dominio que su página, puede hacer lo siguiente:

forceImgReload("myimage.jpg");

Para actualizar una imagen desde otro lugar (dominio cruzado):

forceImgReload("http://someother.server.com/someimage.jpg", true);

Una aplicación más avanzada podría ser volver a cargar una imagen después de cargar una nueva versión en su servidor, preparando la etapa inicial del proceso de recarga simultáneamente con la carga, para minimizar el retraso de recarga visible para el usuario. Si está cargando a través de AJAX, y el servidor está devolviendo una matriz JSON muy simple [éxito, ancho, altura], entonces su código podría verse así:

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

Una nota final: aunque este tema trata sobre imágenes, también se aplica potencialmente a otros tipos de archivos o recursos. Por ejemplo, evitar el uso de secuencias de comandos obsoletas o archivos CSS, o quizás incluso actualizar documentos PDF actualizados (usando (4) solo si está configurado para abrir en el navegador). El método (4) puede requerir algunos cambios en el javascript anterior, en estos casos.

Doin
fuente
Me gusta la idea del método 4, pero no puedes cargar contenido externo con un Iframe, ¿verdad? Actualmente estoy usando el método 3 en una aplicación web de una sola página, pero no me gusta el hecho de que tenga que volver a cargar toda la página para obtener la nueva imagen, incluso si el HTML de la plantilla se vuelve a cargar.
Emilios1995
@Emilios: ... Además, no entiendo tu comentario sobre tener que volver a cargar toda la página. Ambos métodos (3) y (4) se pueden implementar en JavaScript del lado del cliente, sin volver a cargar nada, excepto la imagen que está actualizando. Para el método (3) eso solo significaría usar javascript para cambiar la propiedad 'src' de su imagen de (por ejemplo) image.jpg#123a image.jpg#124(o lo que sea, siempre que cambie el bit después del '#'). ¿Podría aclarar qué es lo que está recargando y por qué?
Doin
@Emilios: De hecho, puede cargar contenido externo (entre dominios) en un iframe ... pero no puede acceder a contentWindowtravés de javascript para hacer la reload(true)llamada que es la parte crítica del método ... así que no, método (4 ) no funcionará para contenido entre dominios. Bien descrito; Actualizaré los "Contras" para incluir eso.
Doin
@Emilios: Vaya, no, no lo haré: me di cuenta de que una solución simple (ahora incluida en mi respuesta) también le permite funcionar para imágenes de dominio cruzado (siempre que pueda poner un script del lado del servidor en su servidor).
Doin
@pseudosavant: Desafortunadamente, me doy cuenta de esto solo ~ 17 meses después, pero lamento tener que decirte que tus ediciones en mi código se rompieron gravemente. (Para ser justos, tampoco creo que el código de devolución de llamada que tenía inicialmente fuera correcto). Ahora he reescrito la parte (4) ampliamente, tanto la explicación como el código. Su código anterior nunca borró las imágenes (por lo que podría fallar de manera extraña, especialmente en IE y especialmente si la imagen se mostraba en varios lugares), pero peor aún, también eliminó el iframe inmediatamente después de comenzar la recarga completa, lo que probablemente significaba que trabaja solo de manera intermitente o no funciona en absoluto. ¡Lo siento!
Doin
185

Como alternativa a ...

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

...parece que...

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

... es suficiente para engañar al caché del navegador sin pasar por alto ningún caché ascendente, suponiendo que haya devuelto los Cache-Controlencabezados correctos . Aunque puedes usar ...

Cache-Control: no-cache, must-revalidate

... pierde los beneficios de los encabezados If-Modified-Sinceo If-None-Match, así que algo así como ...

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

... debería evitar que el navegador vuelva a descargar la imagen completa si no ha cambiado realmente. Probado y trabajando en IE, Firefox y Chrome. Molesto, falla en Safari a menos que uses ...

Cache-Control: no-store

... aunque esto puede ser preferible a llenar cachés en sentido ascendente con cientos de imágenes idénticas, especialmente cuando se ejecutan en su propio servidor. ;-)

Actualización (28-09-2014): hoy en día parece que también Cache-Control: no-storees necesario para Chrome.

Aya
fuente
1
¡Excelente! Después de mucho tiempo tratando de cargar mis imágenes web que se estaban cargando con un retraso, lo acabo de resolver aplicando su solución (con '#', usando '?' No funciona para mí). ¡¡¡Muchas gracias!!!
user304602
18
Aquí hay DOS cachés involucrados: está el caché HTTP normal del navegador y un caché de imágenes en memoria que ha mostrado recientemente. Este último caché en memoria está indexado por el srcatributo completo , por lo que agregar un identificador de fragmento único garantiza que la imagen no se extraiga simplemente de la memoria. Pero los identificadores de fragmentos no se envían como parte de las solicitudes HTTP, por lo que el caché HTTP normal se utilizará de forma normal. Por eso funciona esta técnica.
Doin
Hay varios caché de encabezado. en realidad no sé muy bien inglés, ¿podrían decirme si debo usar cuál? Quiero algo que no almacene en caché solo la foto que se cambia (como un captcha), y almacena en caché otras cosas. entonces Cache-Control: max-age=0, must-revalidatees bueno para mi?
Shafizadeh
A mi no me funciona. Lo único diferente en mi caso es que tengo una URL para una acción de controlador que recupera img de db. Tenía otros argumentos para la acción del controlador, así que lo agregué como "...... & convert = true & t =" + new Date (). GetTime (); y "...... & convert = true #" + new Date (). getTime () ;. ¿Hay algo que estoy haciendo mal?
shaffooo
1
Para evitar la sobrecarga de la creación de objetos y / o la llamada al método, puede usar un número entero incremental como destructor de caché:newImage.src = "http://localhost/image.jpg#" + i++;
laindir
7

Después de crear la nueva imagen, ¿está eliminando la imagen anterior del DOM y reemplazándola por la nueva?

Podrías capturar nuevas imágenes en cada llamada a UpdateImage, pero no agregarlas a la página.

Hay varias formas de hacerlo. Algo como esto funcionaría.

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

Después de que eso funcione, si todavía hay problemas, es probable que se trate de un problema de almacenamiento en caché, como se menciona en las otras respuestas.

BaroqueBobcat
fuente
3

Una respuesta es agregar hackear algunos parámetros de obtención de consultas como se ha sugerido.

Una mejor respuesta es emitir un par de opciones adicionales en su encabezado HTTP.

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

Al proporcionar una fecha en el pasado, el navegador no la almacenará en caché. Cache-Controlse agregó en HTTP / 1.1 y la etiqueta de revalidar debe indicar que los servidores proxy nunca deben mostrar una imagen antigua incluso en circunstancias atenuantes, y esto Pragma: no-cacheno es realmente necesario para los navegadores / cachés modernos actuales, pero puede ayudar con algunas implementaciones antiguas rotas y mal hechas.

Edward KMETT
fuente
3
Parecía que hubiera funcionado, pero aún muestra la misma imagen, incluso con los hacks. Agregaré la información del encabezado a la pregunta.
QueueHammer el
Me acabo de dar cuenta de que estás actualizando la misma etiqueta img una y otra vez. El navegador probablemente esté detectando cuando va a configurar el src que el src no ha cambiado, por lo que no se molesta en actualizar. (Dado que esta comprobación está ocurriendo a nivel DOM y no tiene nada que ver con el objetivo). ¿Qué sucede si agrega "?" + número - a la url de la imagen que se está recuperando?
Edward KMETT el
3

Tenía un requisito: 1) no puedo agregar ninguno ?var=xxa la imagen 2) debería funcionar entre dominios

Realmente me gusta la opción # 4 en esta respuesta con una pero:

  • tiene problemas para trabajar con crossdomain de manera confiable (y requiere tocar el código del servidor).

Mi manera rápida y sucia es:

  1. Crear iframe oculto
  2. Cargue la página actual (sí, toda la página)
  3. iframe.contentWindow.location.reload(true);
  4. Vuelva a configurar la fuente de la imagen en sí mismo

Aquí está

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

Sí, lo sé, setTimeout ... Tienes que cambiar eso a los eventos de carga adecuados.

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

Luego a continuación en algunos 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>

Y lo que esto hace es que, cuando se carga la imagen, programa que se vuelva a cargar en 1 segundo. Estoy usando esto en una página con cámaras de seguridad para el hogar de diferentes tipos.

J Fields
fuente
2

Lo que terminé haciendo fue que el servidor asignara cualquier solicitud de una imagen en ese directorio a la fuente que estaba tratando de actualizar. Luego hice que mi temporizador agregara un número al final del nombre para que el DOM lo viera como una nueva imagen y lo cargara.

P.ej

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

solicitará el mismo código de generación de imágenes pero se verá como imágenes diferentes para el navegador.

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
fuente
8
Esto tendría el mismo problema de almacenar en caché varias copias de la imagen que la solución de cadena de consulta (Paolo y algunas otras), y requiere cambios en el servidor.
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
fuente
3
explique al OP cómo y por qué esto ayuda en lugar de simplemente pegar código
nominativo
No creo que ../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit)sea ​​un nombre de archivo válido ... Al menos esto es lo que intenta cargar ...
ByteHamster
cambiar path='../showImage.php';apath='../showImage.php?';
BOOMik
2
document.getElementById("img-id").src = document.getElementById("img-id").src

establece su propio src como su src.

Hardik
fuente
1

Intente usar una cadena de consulta sin valor para convertirla en una url única:

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
fuente
Agregó el WQS al código y verificó que la solicitud se está aceptando y que el navegador ve que la respuesta proviene de la dirección + WQS sin actualizar la imagen.
QueueHammer el
1

En gran medida basado en el código # 4 de Doin, los siguientes simplifica ejemplo que el código de un gran bits utilizando document.writeen lugar de src en el iframede apoyar CORS. También solo se enfoca en destruir la memoria caché del navegador, no en recargar cada imagen en la página.

A continuación está escrito typescripty utiliza la biblioteca de promesa angular $ q , solo para su información, pero debería ser lo suficientemente fácil de portar a JavaScript vainilla. El método está destinado a vivir dentro de una clase mecanografiada.

Devuelve una promesa que se resolverá cuando el iframe haya completado la recarga. No muy probado, pero funciona bien para nosotros.

    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;
    }
Víctor
fuente
Para protegerse contra los errores XSS, debe usar + encodeURI(src) +para escapar correctamente srcen el iframe.
Tino
1

El siguiente código es útil para actualizar la imagen cuando se hace clic en un botón.

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
fuente
Voto negativo Como es solo una copia ligeramente modificada del código de @ Mahmoud, pero en contraste, esto NO actualiza la imagen
Tino
0

Resolví este problema enviando los datos de vuelta a través de un 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());

Luego, desde la página, simplemente le da el servlet con algunos parámetros para obtener el archivo de imagen correcto.

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

Aquí está mi solución. Es muy simple. La programación del marco podría ser mejor.

<!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
fuente
0

No hay necesidad de new Date().getTime()travesuras. Puede engañar al navegador teniendo una imagen ficticia invisible y usando jQuery .load (), luego creando una nueva imagen cada vez:

<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
fuente
0

Solución simple: agregue este encabezado a la respuesta:

Cache-control: no-store

Por qué esto funciona se explica claramente en esta página autorizada: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

También explica por qué no-cacheno funciona.

Otras respuestas no funcionan porque:

Caching.deletese trata de un nuevo caché que puede crear para el trabajo fuera de línea, consulte: https://web.dev/cache-api-quick-guide/

Los fragmentos que usan un # en la URL no funcionan porque el # le dice al navegador que no envíe una solicitud al servidor.

Un cache-buster con una parte aleatoria agregada a la url funciona, pero también llenará el caché del navegador. En mi aplicación, quería descargar una imagen de 5 MB cada pocos segundos desde una cámara web. Le tomará solo una hora o menos congelar completamente su PC. Todavía no sé por qué la memoria caché del navegador no se limita a un máximo razonable, pero esto definitivamente es una desventaja.

Roland
fuente
0

Mejoré el script de AlexMA para mostrar mi cámara web en una página web que carga periódicamente una nueva imagen con el mismo nombre. Tuve problemas de que a veces la imagen parpadeaba debido a una imagen rota o una imagen cargada no completa (arriba). Para evitar el parpadeo, verifico la altura natural de la imagen porque el tamaño de la imagen de mi cámara web no cambió. Solo si la altura de la imagen cargada se ajusta a la altura de la imagen original, la imagen completa se mostrará en la página.

  <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
fuente
-3

Usé el siguiente concepto de primero vincular la imagen con una URL falsa (buffer) y luego vincularla con la URL válida.

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

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

De esta manera, estoy obligando al navegador a actualizarse con una URL válida.

Prashanth Shivasubramani
fuente