Cómo forzar a un navegador web a NO almacenar imágenes en caché

124

Antecedentes

Estoy escribiendo y usando una herramienta de administración de contenido basada en CGI (Perl) muy simple para dos sitios web pro-bono. Proporciona al administrador del sitio web formularios HTML para eventos en los que rellenan los campos (fecha, lugar, título, descripción, enlaces, etc.) y los guardan. En ese formulario, permito que el administrador cargue una imagen relacionada con el evento. En la página HTML que muestra el formulario, también estoy mostrando una vista previa de la imagen cargada (etiqueta img HTML).

El problema

El problema ocurre cuando el administrador quiere cambiar la imagen. Simplemente tendría que presionar el botón "buscar", elegir una nueva imagen y presionar ok. Y esto funciona bien.

Una vez que se carga la imagen, mi CGI de back-end maneja la carga y vuelve a cargar el formulario correctamente.

El problema es que la imagen que se muestra no se actualiza. La imagen anterior todavía se muestra, aunque la base de datos contiene la imagen correcta. Lo he reducido al hecho de que la IMAGEN ESTÁ CACHADA en el navegador web. Si el administrador presiona el botón RELOAD en Firefox / Explorer / Safari, todo se actualiza bien y la nueva imagen simplemente aparece.

Mi solución: no funciona

Estoy tratando de controlar el caché escribiendo una instrucción HTTP Expires con una fecha muy lejana en el pasado.

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

Recuerde que estoy en el lado administrativo y realmente no me importa si las páginas tardan un poco más en cargarse porque siempre están vencidas.

Pero, esto tampoco funciona.

Notas

Al cargar una imagen, su nombre de archivo no se guarda en la base de datos. Se renombra como Image.jpg (para simplificar las cosas al usarlo). Al reemplazar la imagen existente por una nueva, el nombre tampoco cambia. Solo cambia el contenido del archivo de imagen.

El servidor web es proporcionado por el servicio de alojamiento / ISP. Utiliza Apache.

Pregunta

¿Hay alguna manera de obligar al navegador web a NO almacenar en caché cosas de esta página, ni siquiera imágenes?

Estoy haciendo malabarismos con la opción de "guardar el nombre de archivo" con la base de datos. De esta manera, si se cambia la imagen, el src de la etiqueta IMG también cambiará. Sin embargo, esto requiere muchos cambios en todo el sitio y prefiero no hacerlo si tengo una mejor solución. Además, esto todavía no funcionará si la nueva imagen cargada tiene el mismo nombre (digamos que la imagen se retocó un poco y se volvió a cargar).

Philibert Perusse
fuente
Creo que esta pregunta podría ser retrabajada para eliminar todo el "contexto ahora inútil" que puse allí hace un tiempo cuando lo escribí. Creo que el título es el correcto, y la mejor respuesta también. Pero la pregunta se escribió originalmente para proporcionar mucho espacio para muchos tipos de respuestas y no podría haber esperado una solución tan simple. Por lo tanto, la pregunta es un poco complicada para las personas que vienen allí para obtener la respuesta.
Philibert Perusse

Respuestas:

186

Armin Ronacher tiene la idea correcta. El problema es que las cadenas aleatorias pueden colisionar. Yo usaría:

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

Donde "1222259157.415" es la hora actual en el servidor.
Genere tiempo por Javascript con performance.now()o por Python contime.time()

epochwolf
fuente
32
Una adición importante es que nunca puede forzar a un navegador a hacer nada. Todo lo que puedes hacer es hacer sugerencias amigables. Depende del navegador y del usuario seguir esas sugerencias. Un navegador es libre de ignorar esto, o un usuario podría anular los valores predeterminados.
Joel Coehoorn
8
Joel, habría sido mejor agregar eso en tu propia respuesta.
epochwolf
1
Solo es un pensamiento, y demasiado tarde para venir a la fiesta aquí, pero dado que usted tiene el control del cambio de imagen, tal vez sería mejor cambiar el nombre de la imagen a medida que se actualiza, en lugar de agregar una cadena de consulta. Entonces: 1222259157.jpg por ejemplo, en lugar de picture.jpg? 1222259157. De esa forma se actualiza y se vuelve a almacenar en caché al volver a visitarlo.
danjah
43
En lugar de agregar la hora del servidor, lo que evitará el almacenamiento en caché por completo, ¿por qué no agregar la hora de la última modificación del archivo después de '?'. De esa manera, la imagen se almacenará en caché normalmente hasta que cambie.
Doin
3
¡El último comentario de Doin tiene que ser votado! El almacenamiento en caché inteligente es importante, ¿por qué alguien tendría que descargar una y otra vez el mismo archivo?
f.arcarese
46

Solución simple: adjunte una cadena de consulta aleatoria a la imagen:

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

Lo que dice el RFC de HTTP:

Cache-Control: no-cache

Pero eso no funciona tan bien :)

Armin Ronacher
fuente
1
cómo dar Cache-Control: no-cache en la etiqueta html <img> normal?
P Satish Patro
¿Tiene que venir en respuesta encabezado?
P Satish Patro
22

Utilizo la función de tiempo modificado de archivos de PHP , por ejemplo:

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

Si cambia la imagen, se utiliza la nueva imagen en lugar de la almacenada en caché, debido a que tiene una marca de tiempo modificada diferente.

Almiar
fuente
Gran consejo Tiene tanto el almacenamiento en caché como la imagen reenviados al cliente si su tiempo modificado cambia (sobrescrito o cambiado).
Vasilis Lourdas
ver aquí para otra opción stackoverflow.com/questions/56588850/…
drooh
Absoluto lo clavó. Gran consejo compañero.
Khurram Shaikh
Perfecto ! Solo un consejo adicional: <img src = "images / image.png? <? Php echo filemtime ('images / image.jpg')?>">
Rica Gurgel
16

Yo usaría:

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

donde "20130910043254" es la hora de modificación del archivo.

Al cargar una imagen, su nombre de archivo no se guarda en la base de datos. Se renombra como Image.jpg (para simplificar las cosas cuando se usa). Al reemplazar la imagen existente por una nueva, el nombre tampoco cambia. Solo cambia el contenido del archivo de imagen.

Creo que hay dos tipos de soluciones simples: 1) las que se me ocurren primero (soluciones directas, porque son fáciles de encontrar), 2) las que se terminan después de pensarlo (porque son fáciles de utilizar). Aparentemente, no siempre se beneficiará si elige pensar las cosas. Pero creo que la segunda opción está bastante subestimada. Solo piensa por qué phpes tan popular;)

x-yuri
fuente
1
+1 Creo que es una idea mucho mejor que otras respuestas porque al usar el último tiempo de modificación, su servidor no intentará dar la misma imagen varias veces al mismo navegador cuando no haya cambiado nada en la imagen. Hay un poco de sobrecarga para extraer el último tiempo de escritura de la imagen cada vez que hay una solicitud, pero para una imagen grande, es mejor que tener que devolver la imagen cada vez y cuando la imagen cambia, el usuario tendrá la nueva. Gracias por esta pequeña adición agradable.
Samuel
Bueno, uno podría almacenar el tiempo de modificación junto con la ruta de la imagen en la base de datos. Por lo tanto, la sobrecarga podría ser incluso menos significativa. Por otro lado, si se trata de imágenes que son parte del código fuente del que estamos hablando, también se pueden almacenar en caché sus tiempos de modificación. Al generar un script con tiempos de modificación de imágenes (por ejemplo images.php). Este script debe regenerarse en cada confirmación y elimina la sobrecarga de determinar los tiempos de modificación de los archivos.
x-yuri
@ x-yori es por eso que estoy optando por almacenar este campo actualizado en la base de datos en lugar de ejecutar filemtime stackoverflow.com/questions/56588850/…
drooh
9

use Class = "NO-CACHE"

html de muestra:

<div>
    <img class="NO-CACHE" src="images/img1.jpg" />
    <img class="NO-CACHE" src="images/imgLogo.jpg" />
</div>

jQuery:

    $(document).ready(function ()
    {           
        $('.NO-CACHE').attr('src',function () { return $(this).attr('src') + "?a=" + Math.random() });
    });

javascript:

var nods = document.getElementsByClassName('NO-CACHE');
for (var i = 0; i < nods.length; i++)
{
    nods[i].attributes['src'].value += "?a=" + Math.random();
}

Resultado: src = "images / img1.jpg" => src = "images / img1.jpg? A = 0.08749723793963926"

Aref Rostamkhani
fuente
Simple, elegante y no requiere renderizado del lado del servidor. ¡BONITO!
Ahi Tuna
7

Puede escribir un script proxy para servir imágenes, aunque eso es un poco más de trabajo. Algo le gusta esto:

HTML:

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

Guión:

// PHP
if( isset( $_GET['img'] ) && is_file( IMG_PATH . $_GET['img'] ) ) {

  // read contents
  $f = open( IMG_PATH . $_GET['img'] );
  $img = $f.read();
  $f.close();

  // no-cache headers - complete set
  // these copied from [php.net/header][1], tested myself - works
  header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Some time in the past
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
  header("Cache-Control: no-store, no-cache, must-revalidate"); 
  header("Cache-Control: post-check=0, pre-check=0", false); 
  header("Pragma: no-cache"); 

  // image related headers
  header('Accept-Ranges: bytes');
  header('Content-Length: '.strlen( $img )); // How many bytes we're going to send
  header('Content-Type: image/jpeg'); // or image/png etc

  // actual image
  echo $img;
  exit();
}

En realidad, los encabezados sin caché o el número aleatorio en la imagen src deberían ser suficientes, pero dado que queremos ser a prueba de balas ...

Mindeh
fuente
La suya es una buena solución, excepto que Pragma no es un encabezado de respuesta.
Piskvor salió del edificio el
1
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 parece decir lo contrario.
Grizly
6

Soy un NUEVO codificador, pero esto es lo que se me ocurrió, para evitar que el navegador se almacene en caché y mantenga las vistas de mi cámara web:

<meta Http-Equiv="Cache" content="no-cache">
<meta Http-Equiv="Pragma-Control" content="no-cache">
<meta Http-Equiv="Cache-directive" Content="no-cache">
<meta Http-Equiv="Pragma-directive" Content="no-cache">
<meta Http-Equiv="Cache-Control" Content="no-cache">
<meta Http-Equiv="Pragma" Content="no-cache">
<meta Http-Equiv="Expires" Content="0">
<meta Http-Equiv="Pragma-directive: no-cache">
<meta Http-Equiv="Cache-directive: no-cache">

No estoy seguro de qué funciona en qué navegador, pero funciona para algunos: IE: funciona cuando se actualiza la página web y cuando se vuelve a visitar el sitio web (sin una actualización). CROMO: funciona solo cuando se actualiza la página web (incluso después de una nueva visita). SAFARI y iPad: no funciona, tengo que borrar el historial y los datos web.

¿Alguna idea sobre SAFARI / iPad?

Timmy T.
fuente
4

Al cargar una imagen, su nombre de archivo no se guarda en la base de datos. Se renombra como Image.jpg (para simplificar las cosas cuando se usa).

Cambie esto y habrá solucionado su problema. Utilizo marcas de tiempo, como con las soluciones propuestas anteriormente: Image- <timestamp> .jpg

Presumiblemente, cualquier problema que esté evitando al mantener el mismo nombre de archivo para la imagen se puede superar, pero no dice cuáles son.

AmbroseChapel
fuente
4

Revisé todas las respuestas en la web y la mejor parecía ser: (en realidad no lo es)

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

primero.

Sin embargo, si agrega el parámetro cache = none (que es una palabra estática "none"), no afecta a nada, el navegador aún se carga desde la caché.

La solución a este problema fue:

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

donde básicamente agregas la marca de tiempo de Unix para hacer que el parámetro sea dinámico y sin caché, funcionó.

Sin embargo, mi problema era un poco diferente: estaba cargando sobre la marcha la imagen del gráfico php generada y controlando la página con los parámetros $ _GET. Quería que la imagen se leyera de la caché cuando el parámetro URL GET permanezca igual, y no se guarde en caché cuando cambien los parámetros GET.

Para resolver este problema, necesitaba hash $ _GET pero como es una matriz, aquí está la solución:

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

Editar :

Aunque la solución anterior funciona bien, a veces desea publicar la versión en caché HASTA que se cambie el archivo. (con la solución anterior, deshabilita el caché para esa imagen por completo) Entonces, para servir la imagen en caché desde el navegador HASTA que haya un cambio en el uso del archivo de imagen:

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

filemtime () obtiene la hora de modificación del archivo.

Tarik
fuente
2
La filemtimesolución es mejor también porque md5requiere mucha potencia de procesamiento.
oriadam
2
Pasé días tratando de obtener una aplicación basada en Chromium para detener el almacenamiento en caché de imágenes. El? Nocache con eco de tiempo resolvió el problema. ¡Gracias!
Woody
2

Su problema es que, a pesar del Expires:encabezado, su navegador está reutilizando su copia de la imagen en la memoria antes de que se actualizara, en lugar de verificar su caché.

Tuve una situación muy similar al cargar imágenes de productos en el back-end de administración para un sitio similar a una tienda, y en mi caso decidí que la mejor opción era usar JavaScript para forzar una actualización de imagen, sin usar ninguna de las técnicas de modificación de URL de otras personas Ya he mencionado aquí. En cambio, puse la URL de la imagen en un IFRAME oculto, location.reload(true)invoqué en la ventana del IFRAME y luego reemplacé mi imagen en la página. Esto obliga a actualizar la imagen, no solo en la página en la que estoy, sino también en las páginas posteriores que visito, sin que el cliente o el servidor tengan que recordar ninguna cadena de consulta de URL o parámetros identificadores de fragmentos.

Publiqué un código para hacer esto en mi respuesta aquí .

Doin
fuente
2

Agregar una marca de tiempo <img src="picture.jpg?t=<?php echo time();?>">

siempre le dará a su archivo un número aleatorio al final y lo detendrá

BritishSam
fuente
Vale la pena señalar que la implementación de esta solución pone estrés en el servidor por solicitud y no necesariamente funcionará, ya que el navegador puede almacenar en caché la página php que contiene el tiempo generado en el momento de la solicitud inicial que se almacena en caché. Esto solo funcionaría de manera confiable si la página también tuviera una cadena de consulta dinámica.
Dmitry
1

Con la posibilidad de que los proxy transparentes se comporten mal entre usted y el cliente, la única forma de garantizar totalmente que las imágenes no se almacenarán en caché es dándoles una uri única, algo así como etiquetar una marca de tiempo como una cadena de consulta o como parte de camino.

Si esa marca de tiempo corresponde a la última hora de actualización de la imagen, puede almacenar en caché cuando lo necesite y servir la nueva imagen en el momento justo.

usuario7375
fuente
1

Supongo que la pregunta original se refiere a las imágenes almacenadas con información de texto. Por lo tanto, si tiene acceso a un contexto de texto al generar src = ... url, considere almacenar / usar CRC32 de bytes de imagen en lugar de una marca de tiempo o aleatoria sin sentido. Luego, si se muestra la página con muchas imágenes, solo se recargarán las imágenes actualizadas. Eventualmente, si el almacenamiento de CRC es imposible, se puede calcular y agregar a la url en tiempo de ejecución.

jbw
fuente
O use el tiempo de última modificación de la imagen, en lugar de un CRC, ¡incluso mejor!
Doin
1

Desde mi punto de vista, deshabilitar el almacenamiento en caché de imágenes es una mala idea. En absoluto.

El problema raíz aquí es: cómo forzar al navegador a actualizar la imagen, cuando se ha actualizado en el lado del servidor.

Nuevamente, desde mi punto de vista personal, la mejor solución es desactivar el acceso directo a las imágenes. En su lugar, acceda a las imágenes a través del filtro del servidor / servlet / otras herramientas / servicios similares.

En mi caso, es un servicio de descanso, que devuelve una imagen y adjunta ETag en respuesta. El servicio mantiene el hash de todos los archivos, si el archivo se cambia, el hash se actualiza. Funciona perfectamente en todos los navegadores modernos. Sí, lleva tiempo implementarlo, pero vale la pena.

La única excepción son los favicons. Por algunas razones, no funciona. No pude forzar al navegador a actualizar su caché desde el lado del servidor. ETags, Cache Control, Expires, Pragma headers, nada ayudó.

En este caso, parece que agregar un parámetro aleatorio / versión a la url es la única solución.

Alexandr
fuente
1

Idealmente, debe agregar un botón / combinación de teclas / menú a cada página web con una opción para sincronizar el contenido.

Para hacerlo, debe realizar un seguimiento de los recursos que pueden necesitar sincronizarse, y usar xhr para sondear las imágenes con una cadena de consulta dinámica, o crear una imagen en tiempo de ejecución con src usando una cadena de consulta dinámica. Luego, use un mecanismo de transmisión para notificar a todos los componentes de las páginas web que están utilizando el recurso para actualizar para usar el recurso con una cadena de consulta dinámica añadida a su url.

Un ejemplo ingenuo se ve así:

Normalmente, la imagen se muestra y almacena en caché, pero si el usuario presionó el botón, se envía una solicitud xhr al recurso con una cadena de consulta de tiempo adjunta; dado que se puede suponer que el tiempo es diferente en cada pulsación, se asegurará de que el navegador omita la memoria caché, ya que no puede determinar si el recurso se genera dinámicamente en el lado del servidor en función de la consulta, o si es estático recurso que ignora la consulta.

El resultado es que puede evitar que todos sus usuarios lo bombardeen con solicitudes de recursos todo el tiempo, pero al mismo tiempo, permita un mecanismo para que los usuarios actualicen sus recursos si sospechan que no están sincronizados.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="mobile-web-app-capable" content="yes" />        
    <title>Resource Synchronization Test</title>
    <script>
function sync() {
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {            
            var images = document.getElementsByClassName("depends-on-resource");

            for (var i = 0; i < images.length; ++i) {
                var image = images[i];
                if (image.getAttribute('data-resource-name') == 'resource.bmp') {
                    image.src = 'resource.bmp?i=' + new Date().getTime();                
                }
            }
        }
    }
    xhr.open('GET', 'resource.bmp', true);
    xhr.send();
}
    </script>
  </head>
  <body>
    <img class="depends-on-resource" data-resource-name="resource.bmp" src="resource.bmp"></img>
    <button onclick="sync()">sync</button>
  </body>
</html>
Dmitry
fuente
0

Debe usar un nombre de archivo único. Me gusta esto

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

Pero esta técnica significa un alto uso del servidor y un desperdicio de ancho de banda. En su lugar, debe usar el número de versión o la fecha. Ejemplo:

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

Pero desea que nunca sirva la imagen de la memoria caché. Para esto, si la página no usa caché de página , es posible con PHP o el lado del servidor.

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

Sin embargo, todavía no es efectivo. Motivo: caché del navegador ... El último pero más efectivo método es Native JAVASCRIPT. Este código simple encuentra todas las imágenes con una clase "NO-CACHE" y hace que las imágenes sean casi únicas. Ponga esto entre las etiquetas de script.

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

USO

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

RESULTADO (s) como este

https://example.com/image.png?1582018163634
Ali
fuente