Como forçar um navegador da Web a NÃO armazenar imagens em cache

124

fundo

Estou escrevendo e usando uma ferramenta muito simples de gerenciamento de conteúdo baseada em CGI (Perl) para dois sites gratuitos. Ele fornece ao administrador do site formulários HTML para eventos nos quais eles preenchem os campos (data, local, título, descrição, links, etc.) e os salvam. Nesse formulário, permito que o administrador envie uma imagem relacionada ao evento. Na página HTML que exibe o formulário, também estou mostrando uma visualização da imagem carregada (tag img HTML).

O problema

O problema ocorre quando o administrador deseja alterar a imagem. Ele apenas precisaria clicar no botão "navegar", escolher uma nova foto e pressionar ok. E isso funciona bem.

Depois que a imagem é carregada, meu CGI de back-end lida com o upload e recarrega o formulário corretamente.

O problema é que a imagem mostrada não é atualizada. A imagem antiga ainda é mostrada, mesmo que o banco de dados mantenha a imagem correta. Eu reduzi ao fato de que a IMAGEM É CACHE no navegador da web. Se o administrador pressionar o botão RELOAD no Firefox / Explorer / Safari, tudo será atualizado corretamente e a nova imagem aparecerá.

Minha solução - Não está funcionando

Estou tentando controlar o cache, escrevendo uma instrução HTTP Expires com uma data muito distante no passado.

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

Lembre-se de que eu sou do lado administrativo e realmente não me importo se as páginas demorarem um pouco mais para carregar, porque sempre expiram.

Mas isso também não funciona.

Notas

Ao fazer upload de uma imagem, seu nome de arquivo não é mantido no banco de dados. Ele é renomeado como Image.jpg (para simplificar as coisas ao usá-lo). Ao substituir a imagem existente por uma nova, o nome também não muda. Apenas o conteúdo do arquivo de imagem é alterado.

O servidor da web é fornecido pelo serviço de hospedagem / ISP. Ele usa o Apache.

Questão

Existe uma maneira de forçar o navegador da Web a NÃO armazenar em cache coisas desta página, nem mesmo imagens?

Estou manipulando a opção de realmente "salvar o nome do arquivo" no banco de dados. Dessa forma, se a imagem for alterada, o src da tag IMG também será alterado. No entanto, isso requer muitas alterações em todo o site e prefiro não fazê-lo se tiver uma solução melhor. Além disso, isso ainda não funcionará se a nova imagem carregada tiver o mesmo nome (digamos que a imagem tenha sido photoshopada um pouco e carregada novamente).

Philibert Perusse
fonte
Eu acho que essa pergunta poderia ser reformulada para remover todo o "contexto agora inútil" que eu coloquei lá um tempo atrás quando a escrevi. Eu acho que o título é o certo, e a resposta principal também. Mas a pergunta foi escrita originalmente para fornecer muito espaço para muitos tipos de respostas e não poderia esperar uma solução tão simples. Portanto, a pergunta é um pouco complicada para as pessoas que estão lá para obter a resposta.
Philibert Perusse

Respostas:

186

Armin Ronacher tem a ideia correta. O problema é que seqüências aleatórias podem colidir. Eu usaria:

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

Onde "1222259157.415" é a hora atual no servidor.
Gere tempo por Javascript com performance.now()ou por Python comtime.time()

epochwolf
fonte
32
Uma adição importante é que você nunca pode forçar um navegador a fazer nada. Tudo o que você pode fazer é fazer sugestões amigáveis. Cabe ao navegador e ao usuário realmente seguir essas sugestões. Um navegador é livre para ignorar isso ou um usuário pode substituir os padrões.
Joel Coehoorn
8
Joel, seria melhor adicionar isso em sua própria resposta.
epochwolf 24/09/08
1
Apenas um pensamento e tarde demais para vir para a festa aqui - mas como você tem o controle da alteração da imagem, talvez seja melhor renomear a imagem conforme ela é atualizada, em vez de adicionar uma sequência de consulta. Então: 1222259157.jpg por exemplo, em vez de picture.jpg? 1222259157. Dessa forma, ele é atualizado e, novamente, armazenado em cache após a revisitação.
danjah
43
Em vez de adicionar a hora do servidor, o que impedirá o armazenamento em cache completo, por que não adicionar a hora da última modificação do arquivo após o '?' Dessa forma, a imagem será armazenada em cache normalmente até a próxima alteração.
Doin
3
O último comentário de Doin precisa ser votado! O cache inteligente é realmente importante, por que alguém precisaria baixar repetidamente o mesmo arquivo?
23414
46

Correção simples: anexe uma string de consulta aleatória à imagem:

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

O que o RFC HTTP diz:

Cache-Control: no-cache

Mas isso não funciona tão bem :)

Armin Ronacher
fonte
1
como dar Cache-Control: sem cache na tag html normal <img>?
P Satish Patro
Precisa vir no cabeçalho de resposta?
P Satish Patro
22

Eu uso a função de hora modificada de arquivo do PHP , por exemplo:

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

Se você alterar a imagem, a nova imagem será usada em vez da em cache, devido a um carimbo de data / hora modificado diferente.

Rick
fonte
Ótima dica. Você tem o cache e a imagem reenviados ao cliente se o horário modificado mudar (sobrescrito ou alterado).
Vasilis Lourdas
veja aqui para outra opção stackoverflow.com/questions/56588850/…
drooh 19/06/19
Absolute acertou em cheio. Ótima dica.
Khurram Shaikh
Perfeito! Apenas uma dica extra: <img src = "images / image.png? <? Php echo filemtime ('images / image.jpg')?>">
Rica Gurgel
16

Eu usaria:

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

onde "20130910043254" é o horário da modificação do arquivo.

Ao fazer upload de uma imagem, seu nome de arquivo não é mantido no banco de dados. Ele é renomeado como Image.jpg (para simplificar as coisas ao usá-lo). Ao substituir a imagem existente por uma nova, o nome também não muda. Apenas o conteúdo do arquivo de imagem é alterado.

Eu acho que existem dois tipos de soluções simples: 1) aquelas que vêm à mente primeiro (soluções diretas, porque são fáceis de encontrar); usar). Aparentemente, você nem sempre será beneficiado se optar por pensar sobre as coisas. Mas a segunda opção é subestimada, acredito. Basta pensar por que phpé tão popular;)

x-yuri
fonte
1
+1 Acho que é uma ideia muito melhor do que outras respostas, porque, usando o tempo de modificação da las, seu servidor não tentará fornecer a mesma imagem várias vezes ao mesmo navegador quando nada tiver mudado na imagem. Há um pouco de sobrecarga para extrair o último tempo de gravação da imagem sempre que houver uma solicitação, mas para uma imagem grande, é melhor do que ter que retornar a imagem a cada vez e, quando a imagem mudar, o usuário terá a nova. Obrigado por esta pequena adição agradável.
Samuel
Bem, pode-se armazenar o tempo de modificação junto com o caminho para a imagem no banco de dados. Portanto, a sobrecarga pode ser ainda menos significativa. Por outro lado, se estas são imagens que fazem parte do código fonte sobre o qual estamos falando, também é possível armazenar em cache seus tempos de modificação. Gerando um script com tempos de modificação de imagens (por exemplo images.php). Esse script deve ser regenerado a cada confirmação e elimina a sobrecarga na determinação dos tempos de modificação dos arquivos.
X-yuri
@ x-yori é por isso que estou optando por armazenar este campo atualizado no banco de dados ao invés de correr filemtime stackoverflow.com/questions/56588850/...
drooh
9

use Class = "NO-CACHE"

amostra html:

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

jQuery:

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

javascript:

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

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

Aref Rostamkhani
fonte
Simples, elegante e não requer renderização no servidor. LEGAIS!
Ahi Tuna
7

Você pode escrever um script proxy para veicular imagens - embora isso seja um pouco mais trabalhoso. Algo gosta disso:

HTML:

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

Roteiro:

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

Na verdade, cabeçalhos sem cache ou número aleatório na imagem src devem ser suficientes, mas como queremos ser à prova de balas.

Mindeh
fonte
A sua é uma boa solução, exceto que o Pragma não é um cabeçalho de resposta.
Piskvor saiu do prédio 15/01/09
1
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 parece dizer o contrário.
Grizly
6

Sou um NOVO codificador, mas eis o que eu criei para impedir que o navegador armazene em cache e mantenha minhas visualizações da webcam:

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

Não tenho certeza do que funciona em qual navegador, mas funciona para alguns: IE: funciona quando a página da Web é atualizada e quando o site é revisitado (sem atualização). CHROME: funciona apenas quando a página da web é atualizada (mesmo após uma nova visita). SAFARI e iPad: Não funciona, preciso limpar o histórico e os dados da web.

Alguma idéia sobre SAFARI / iPad?

Timmy T.
fonte
4

Ao fazer upload de uma imagem, seu nome de arquivo não é mantido no banco de dados. Ele é renomeado como Image.jpg (para simplificar as coisas ao usá-lo).

Mude isso e você resolveu o seu problema. Uso carimbos de data e hora, como nas soluções propostas acima: Image- <timestamp> .jpg

Presumivelmente, quaisquer problemas que você esteja evitando mantendo o mesmo nome de arquivo para a imagem podem ser superados, mas você não diz o que são.

AmbroseChapel
fonte
4

Eu verifiquei todas as respostas na web e a melhor parecia ser: (na verdade não é)

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

no início.

No entanto, se você adicionar o parâmetro cache = none (que é a palavra estática "none"), isso não afetará nada, o navegador ainda será carregado do cache.

A solução para esse problema foi:

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

onde você basicamente adiciona timestamp unix para tornar o parâmetro dinâmico e sem cache, funcionou.

No entanto, meu problema era um pouco diferente: eu estava carregando rapidamente a imagem do gráfico php gerado e controlando a página com os parâmetros $ _GET. Eu queria que a imagem fosse lida no cache quando o parâmetro URL GET permanece o mesmo e não é armazenada em cache quando os parâmetros GET são alterados.

Para resolver esse problema, eu precisava fazer o hash $ _GET, mas, como é o array, aqui está a solução:

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

Editar :

Embora a solução acima funcione bem, às vezes você deseja exibir a versão em cache ATÉ que o arquivo seja alterado. (com a solução acima, desativa completamente o cache dessa imagem). Portanto, para exibir a imagem em cache do navegador ATÉ que haja uma alteração no uso do arquivo de imagem:

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

filemtime () obtém o tempo de modificação do arquivo.

Tarik
fonte
2
A filemtimesolução é melhor também porque md5requer muito poder de processamento.
oriadam
2
Passou dias tentando fazer com que o aplicativo baseado no Chromium parasse de armazenar imagens em cache. O eco nocache com o tempo resolveu o problema. Obrigado!
Woody
2

Seu problema é que, apesar do Expires:cabeçalho, seu navegador está reutilizando sua cópia na imagem da memória antes de ser atualizada, em vez de até mesmo verificar seu cache.

Tive uma situação muito semelhante ao carregar imagens de produtos no back-end de administrador de um site semelhante a uma loja e, no meu caso, decidi que a melhor opção era usar javascript para forçar uma atualização de imagem, sem usar nenhuma das técnicas de modificação de URL de outras pessoas. já mencionei aqui. Em vez disso, coloquei o URL da imagem em um IFRAME oculto, chamado location.reload(true)na janela do IFRAME e, em seguida, substitui minha imagem na página. Isso força uma atualização da imagem, não apenas na página em que estou, mas também nas páginas posteriores que visito - sem que o cliente ou o servidor precise lembrar de qualquer parâmetro de string de consulta ou identificador de fragmento da URL.

Publiquei algum código para fazer isso na minha resposta aqui .

Doin
fonte
2

Adicionar carimbo de hora <img src="picture.jpg?t=<?php echo time();?>">

sempre dará ao seu arquivo um número aleatório no final e interromperá o cache

BritishSam
fonte
Vale ressaltar que a implementação desta solução coloca pressão no servidor por solicitação e não funcionará necessariamente, pois o navegador pode armazenar em cache a página php que contém o tempo gerado no momento da solicitação inicial que é armazenada em cache. Isso funcionaria apenas de maneira confiável se a página também tivesse uma sequência de consultas dinâmica.
Dmitry
1

Com o potencial de proxies transparentes com mau comportamento entre você e o cliente, a única maneira de garantir totalmente que as imagens não serão armazenadas em cache é fornecendo a eles uma uri exclusiva, algo como marcar um carimbo de data e hora como uma string de consulta ou como parte do caminho.

Se esse registro de data e hora corresponder ao horário da última atualização da imagem, você poderá armazenar em cache quando precisar e veicular a nova imagem no momento certo.

user7375
fonte
1

Suponho que a pergunta original seja referente às imagens armazenadas com algumas informações de texto. Portanto, se você tiver acesso a um contexto de texto ao gerar src = ... url, considere armazenar / usar CRC32 de bytes de imagem em vez de aleatório ou carimbo de data / hora sem sentido. Então, se a página com muitas imagens estiver sendo exibida, apenas as imagens atualizadas serão recarregadas. Eventualmente, se o armazenamento CRC for impossível, ele poderá ser calculado e anexado ao URL no tempo de execução.

jbw
fonte
Ou use o horário da última modificação da imagem, em vez de um CRC, ainda melhor!
Doin
1

Do meu ponto de vista, desabilitar o cache de imagens é uma má idéia. Em absoluto.

O problema principal aqui é - como forçar o navegador a atualizar a imagem, quando ela for atualizada no servidor.

Novamente, do meu ponto de vista pessoal, a melhor solução é desativar o acesso direto às imagens. Em vez disso, acesse imagens via filtro / servlet / outras ferramentas / serviços do lado do servidor.

No meu caso, é um serviço de descanso, que retorna imagem e anexa ETag em resposta. O serviço mantém o hash de todos os arquivos; se o arquivo for alterado, o hash será atualizado. Funciona perfeitamente em todos os navegadores modernos. Sim, leva tempo para implementá-lo, mas vale a pena.

A única exceção - são os favicons. Por algumas razões, isso não funciona. Não pude forçar o navegador a atualizar seu cache do lado do servidor. ETags, controle de cache, expira, cabeçalhos Pragma, nada ajudou.

Nesse caso, adicionar algum parâmetro aleatório / versão ao url, ao que parece, é a única solução.

Alexandr
fonte
1

Idealmente, você deve adicionar um botão / combinação de teclas / menu a cada página da Web com uma opção para sincronizar o conteúdo.

Para fazer isso, você deve acompanhar os recursos que precisam ser sincronizados e usar o xhr para investigar as imagens com uma sequência de consultas dinâmica ou criar uma imagem em tempo de execução com src usando uma sequência de consultas dinâmica. Em seguida, use um mecanismo de transmissão para notificar todos os componentes das páginas da Web que estão usando o recurso para atualizar para usar o recurso com uma cadeia de consulta dinâmica anexada ao seu URL.

Um exemplo ingênuo é assim:

Normalmente, a imagem é exibida e armazenada em cache, mas se o usuário pressionar o botão, uma solicitação xhr será enviada ao recurso com uma cadeia de consulta de tempo anexada a ele; como o tempo pode ser diferente em cada impressora, ele garantirá que o navegador ignore o cache, pois não pode dizer se o recurso é gerado dinamicamente no servidor com base na consulta ou se é estático. recurso que ignora a consulta.

O resultado é que você pode evitar que todos os seus usuários o bombardeiem com solicitações de recursos o tempo todo, mas, ao mesmo tempo, permita um mecanismo para que os usuários atualizem seus recursos se suspeitarem que estão fora de sincronia.

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

Você deve usar um nome de arquivo exclusivo. Como isso

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

Mas essa técnica significa alto uso do servidor e desperdício de largura de banda. Em vez disso, você deve usar o número da versão ou a data. Exemplo:

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

Mas você deseja que ela nunca sirva a imagem do cache. Para isso, se a página não usar cache de página , é possível com o PHP ou o servidor.

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

No entanto, ainda não é eficaz. Motivo: cache do navegador ... O último, mas mais eficaz, é o JAVASCRIPT nativo. Este código simples localiza todas as imagens com uma classe "NO-CACHE" e torna as imagens quase únicas. Coloque isso entre as tags 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="">

Resultados como este

https://example.com/image.png?1582018163634
Todos
fonte