Atualizar imagem com uma nova no mesmo URL

333

Estou acessando um link no meu site que fornecerá uma nova imagem sempre que for acessada.

O problema que eu estou enfrentando é que, se eu tentar carregar a imagem em segundo plano e atualizar a imagem da página, a imagem não será alterada - embora seja atualizada quando eu recarregar a 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);
}

Cabeçalhos como o FireFox os vê:

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

Preciso forçar uma atualização apenas dessa imagem na página. Alguma ideia?

QueueHammer
fonte

Respostas:

351

Tente adicionar um quebra-cache no final do URL:

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

Isso acrescentará o carimbo de data / hora atual automaticamente quando você estiver criando a imagem e fará com que o navegador procure novamente a imagem em vez de recuperar a que está no cache.

Paolo Bergantino
fonte
26
Não é uma solução muito boa, pois irá inundar os caches (locais e upstream). A resposta de Aya tem uma maneira melhor de lidar com isso.
Tgr
1
Além disso, exibir novamente a mesma imagem em outro lugar, sem o "cache breaker" mais tarde, ainda mostra a versão antiga em cache (pelo menos no firefox)? e # :(
T4NK3R
3
Você pode criar ainda menos código com isso:'image.jpg?' + (+new Date())
Lev Lukomsky
4
Existe Date.now()para isso
vp_arth
2
Por que nãoMath.random()
Gowtham Gopalakrishnan
227

Eu já vi muitas variações de respostas sobre como fazer isso, então pensei em resumi-las aqui (além de adicionar um quarto método de minha própria invenção):


(1) Adicione um parâmetro exclusivo de consulta de impedimento de cache ao URL, como:

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

Prós: 100% confiável, rápido e fácil de entender e implementar.

Contras: ignora completamente o cache, significando atrasos desnecessários e uso de largura de banda sempre que a imagem não muda entre as visualizações. Potencialmente preencherá o cache do navegador (e quaisquer caches intermediários) com muitas, muitas cópias da mesma imagem! Além disso, requer a modificação do URL da imagem.

Quando usar: use quando a imagem estiver mudando constantemente, como para um feed de webcam ao vivo. Se você usar esse método, certifique-se de veicular as imagens com Cache-control: no-cachecabeçalhos HTTP !!! (Geralmente, isso pode ser configurado usando um arquivo .htaccess). Caso contrário, você estará progressivamente preenchendo caches com versões antigas da imagem!


(2) Adicione um parâmetro de consulta ao URL que muda apenas quando o arquivo é alterado, por exemplo:

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

(Esse é o código do lado do servidor PHP, mas o ponto importante aqui é apenas que uma string de consulta ? M = [hora da última modificação do arquivo] é anexada ao nome do arquivo).

Prós: 100% confiável, rápido e fácil de entender e implementar, e preserva perfeitamente as vantagens do cache.

Contras: Requer a modificação do URL da imagem. Além disso, um pouco mais de trabalho para o servidor - ele precisa ter acesso ao horário da última modificação do arquivo. Além disso, exige informações do lado do servidor, portanto, não é adequado para uma solução exclusivamente do lado do cliente verificar uma imagem atualizada.

Quando usar: quando você deseja armazenar em cache imagens, mas pode precisar atualizá-las periodicamente no servidor, sem alterar o nome do arquivo. E quando você pode garantir facilmente que a string de consulta correta seja adicionada a cada instância de imagem no seu HTML.


(3) Sirva suas imagens com o cabeçalho Cache-control: max-age=0, must-revalidatee adicione à URL um identificador de fragmento exclusivo para armazenamento de memcache , como:

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

A idéia aqui é que o cabeçalho de controle de cache coloque imagens no cache do navegador, mas imediatamente as coloque obsoletas, para que, e sempre que sejam exibidas novamente, o navegador verifique com o servidor se elas foram alteradas. Isso garante que o cache HTTP do navegador sempre retorne a cópia mais recente da imagem. No entanto, os navegadores geralmente reutilizam uma cópia na memória de uma imagem, se tiverem uma, e nem mesmo verificam seu cache HTTP nesse caso. Para evitar isso, um identificador de fragmento é usado: A comparação das imagens na memória srcinclui o identificador de fragmento, mas ele é retirado antes de consultar o cache HTTP. (Por exemplo, image.jpg#Ae image.jpg#Bpodem ser exibidos a partir da image.jpgentrada no cache HTTP do navegador, masimage.jpg#Bnunca seria exibido usando dados de imagem retidos na memória desde a image.jpg#Aúltima vez que foi exibido).

Prós: faz uso adequado dos mecanismos de cache HTTP e usa imagens em cache se elas não foram alteradas. Funciona para servidores que engasgam com uma string de consulta adicionada a uma URL de imagem estática (já que os servidores nunca veem identificadores de fragmento - eles são apenas para uso do próprio navegador).

Contras: Depende do comportamento um tanto duvidoso (ou pelo menos mal documentado) dos navegadores em relação às imagens com identificadores de fragmentos em seus URLs (no entanto, testei isso com êxito no FF27, Chrome33 e IE11). Ainda envia uma solicitação de revalidação ao servidor para cada exibição de imagem, o que pode ser um exagero se as imagens forem alteradas raramente e / ou a latência for um grande problema (pois é necessário aguardar a resposta de revalidação, mesmo quando a imagem em cache ainda estiver boa) . Requer a modificação de URLs de imagem.

Quando usar: use quando as imagens podem mudar com freqüência ou precisam ser atualizadas de forma intermitente pelo cliente, sem o envolvimento do script do servidor, mas onde você ainda deseja a vantagem do armazenamento em cache. Por exemplo, pesquisar uma webcam ao vivo que atualiza uma imagem irregularmente a cada poucos minutos. Como alternativa, use em vez de (1) ou (2) se o servidor não permitir cadeias de consulta em URLs de imagem estática.


(4) Atualize à força uma imagem específica usando Javascript, primeiro carregando-a em uma oculta <iframe>e depois chamando location.reload(true)a iframe contentWindow.

Os passos são:

  • Carregue a imagem a ser atualizada em um iframe oculto. Esta é apenas uma etapa da configuração - pode ser feita com muito antecedência a atualização real, se desejado. Não importa se a imagem falha ao carregar nesta fase!

  • Feito isso, esvazie todas as cópias dessa imagem em suas páginas ou em qualquer lugar em qualquer nó DOM (mesmo fora da página armazenado em variáveis ​​javascript). Isso é necessário porque o navegador pode exibir a imagem de uma cópia antiga na memória (o IE11 faz isso especialmente): É necessário garantir que todas as cópias na memória sejam limpas antes de atualizar o cache HTTP. Se outro código javascript estiver sendo executado de forma assíncrona, talvez você também precise impedir que esse código crie novas cópias da imagem a ser atualizada enquanto isso.

  • Ligue iframe.contentWindow.location.reload(true). As trueforças de um desvio cache, recarregar diretamente do servidor e substituindo a cópia em cache existente.

  • Assim que terminar re -loading, restaurar as imagens em branco. Agora eles devem exibir a versão nova do servidor!

Para imagens do mesmo domínio, você pode carregar a imagem diretamente no iframe. Para imagens entre domínios, você deve carregar uma página HTML do seu domínio que contenha a imagem em uma <img>tag, caso contrário, você receberá um erro "Acesso negado" ao tentar ligar iframe.contentWindow.reload(...).

Prós: Funciona exatamente como a função image.reload () que você deseja que o DOM possua ! Permite que as imagens sejam armazenadas em cache normalmente (mesmo com datas de validade futuras, se você as desejar, evitando assim a revalidação frequente). Permite atualizar uma imagem específica sem alterar os URLs dessa imagem na página atual ou em qualquer outra página, usando apenas o código do lado do cliente.

Contras: depende de Javascript. Não é 100% garantido que funcione corretamente em todos os navegadores (embora eu tenha testado isso com êxito no FF27, Chrome33 e IE11). Muito complicado em relação aos outros métodos.

Quando usar: quando você tem uma coleção de imagens basicamente estáticas que deseja armazenar em cache, mas ainda precisa atualizá-las ocasionalmente e obter feedback visual imediato de que a atualização ocorreu. (Especialmente quando apenas atualizar a página inteira do navegador não funcionaria, como em alguns aplicativos da Web criados no AJAX, por exemplo). E quando os métodos (1) - (3) não são viáveis, porque (por qualquer motivo) você não pode alterar todos os URLs que podem exibir a imagem que você precisa atualizar. (Observe que, usando esses três métodos, a imagem será atualizada, mas se outra página tentar exibir essa imagem sem a string de consulta ou o identificador de fragmento apropriado, ela poderá mostrar uma versão mais antiga).

Os detalhes de implementar isso de uma maneira robusta e flexível são dados abaixo:

Vamos supor que seu site contenha .gif em branco de 1x1 pixel no caminho da URL /img/1x1blank.gife também tenha o seguinte script PHP de uma linha (necessário apenas para aplicar atualização forçada a imagens entre domínios) e pode ser reescrito em qualquer linguagem de script do lado do servidor , é claro) no caminho da URL /echoimg.php:

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

Então, aqui está uma implementação realista de como você pode fazer tudo isso em Javascript. Parece um pouco complicado, mas há muitos comentários, e a função importante é apenas forceImgReload () - as duas primeiras imagens em branco e não em branco, e devem ser projetadas para funcionar eficientemente com seu próprio HTML, codifique-as como funciona melhor para você; muitas das complicações nelas podem ser desnecessárias para o seu site:

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

Em seguida, para forçar uma atualização de uma imagem localizada no mesmo domínio da sua página, você pode:

forceImgReload("myimage.jpg");

Para atualizar uma imagem de outro lugar (entre domínios):

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

Um aplicativo mais avançado pode ser recarregar uma imagem após o upload de uma nova versão para o servidor, preparando o estágio inicial do processo de recarregamento simultaneamente com o upload, para minimizar o atraso visível do recarregamento para o usuário. Se você estiver fazendo o upload via AJAX, e o servidor estiver retornando uma matriz JSON muito simples [sucesso, largura, altura], seu código poderá se parecer com o seguinte:

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

Uma observação final: embora este tópico seja sobre imagens, ele também se aplica a outros tipos de arquivos ou recursos. Por exemplo, impedindo o uso de scripts obsoletos ou arquivos css, ou mesmo atualizando documentos PDF atualizados (usando (4) somente se configurado para abrir no navegador). O método (4) pode exigir algumas alterações no javascript acima, nesses casos.

Doin
fonte
Gosto da ideia do método 4, mas você não pode carregar conteúdo externo com um Iframe, pode? Atualmente, estou usando o método 3 em um aplicativo Web de página única, mas não gosto que você precise recarregar a página inteira para obter a nova imagem, mesmo que o HTML do modelo seja recarregado.
Emilios1995
@Emilios: ... Além disso, não entendo seu comentário sobre ter que recarregar a página inteira. Ambos os métodos (3) e (4) podem ser implementados em javascript do lado do cliente, sem recarregar nada, exceto a imagem que você está atualizando. Para o método (3), isso significa apenas o uso de javascript para alterar a propriedade 'src' da imagem de (por exemplo) image.jpg#123para image.jpg#124(ou qualquer outra coisa, desde que o bit após o '#' seja alterado). Você poderia esclarecer o que está recarregando e por quê?
Doin
@Emilios: Você pode realmente carregar conteúdo externo (entre domínios) em um iframe ... mas você não pode acessá-lo contentWindowvia javascript para fazer a reload(true)chamada, que é a parte crítica do método ... então não, método (4 ) não funcionará para conteúdo entre domínios. Bem manchado; Vou atualizar os "Contras" para incluir isso.
Doin
@Emilios: Opa, não, não vou: percebi que uma correção simples (agora incluída na minha resposta) permite que funcione também para imagens entre domínios (desde que você possa colocar um script do lado do servidor no servidor).
dec
@pseudosavant: Infelizmente, estou percebendo isso apenas ~ 17 meses depois, mas lamento ter que lhe dizer que suas edições no meu código foram muito danificadas. (Para ser justo, também não acho que o código de retorno de chamada que eu tinha inicialmente). Agora reescrevi a parte (4) extensivamente, tanto a explicação quanto o código. Seu código anterior nunca apagou as imagens (por isso, pode falhar de maneiras estranhas, especialmente no IE e principalmente se a imagem foi mostrada em vários lugares), mas pior ainda, excluiu o iframe imediatamente após iniciar a recarga completa, o que provavelmente significava que trabalhe apenas de forma intermitente ou não funcione. Desculpe!
Doin
185

Como alternativa para ...

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

...parece que...

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

... é suficiente para enganar o cache do navegador sem ignorar os caches upstream, assumindo que você retornou os Cache-Controlcabeçalhos corretos . Embora você possa usar ...

Cache-Control: no-cache, must-revalidate

... você perde os benefícios dos cabeçalhos If-Modified-Sinceou If-None-Match, então algo como ...

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

... deve impedir o navegador de baixar novamente a imagem inteira, se ela não tiver sido realmente alterada. Testado e funcionando no IE, Firefox e Chrome. Irritantemente, ele falha no Safari, a menos que você use ...

Cache-Control: no-store

... embora isso ainda possa ser preferível ao preenchimento de caches upstream com centenas de imagens idênticas, principalmente quando elas estão sendo executadas em seu próprio servidor. ;-)

Atualização (28-09-2014): hoje em dia parece Cache-Control: no-storeser necessário também para o Chrome.

Aya
fonte
1
Ótimo! Depois de muito tempo tentando carregar minhas imagens da web que estavam sendo carregadas com um atraso, eu a resolvi aplicando sua solução (com '#', usando '?' Não está funcionando para mim). Muito Obrigado!!!
user304602
18
Existem DOIS caches envolvidos aqui: há o cache HTTP normal do navegador e um cache de imagens na memória que foi exibido recentemente. Esse último cache da memória é indexado pelo srcatributo completo , portanto, adicionar um identificador de fragmento exclusivo garante que a imagem não seja simplesmente retirada da memória. Mas os identificadores de fragmento não são enviados como parte das solicitações HTTP, portanto, o cache HTTP comum será usado normalmente. É por isso que essa técnica funciona.
Doin
Há vários cache de cabeçalho. na verdade, eu não sei muito bem inglês, você pode me dizer se devo usar qual ?! Eu quero algo que não armazene em cache apenas a foto que é alterada (como um captcha) e armazene em cache outras coisas. então Cache-Control: max-age=0, must-revalidateé bom pra mim?
Shafizadeh
Isso não funciona para mim. A única coisa diferente no meu caso é que eu tenho um URL para uma ação do controlador que recupera img do db. Eu tinha outros argumentos para a ação do controlador, então a adicionei como "...... & convert = true & t =" + new Date (). GetTime (); e "...... & convert = true #" + nova data (). getTime () ;. Existe algo que estou fazendo de errado?
Shaffooo
1
Para evitar a sobrecarga de criação do objeto e / ou chamada de método, você poderia usar um inteiro incrementando como o cache-buster:newImage.src = "http://localhost/image.jpg#" + i++;
laindir
7

Depois de criar a nova imagem, você está removendo a imagem antiga do DOM e substituindo-a pela nova?

Você pode pegar novas imagens a cada chamada updateImage, mas não adicioná-las à página.

Existem várias maneiras de fazer isso. Algo assim iria funcionar.

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

Depois de fazer isso funcionar, se ainda houver problemas, provavelmente é um problema de cache, como as outras respostas mencionam.

BarrocoBobcat
fonte
3

Uma resposta é adicionar hackishly algum parâmetro get query como foi sugerido.

Uma resposta melhor é emitir algumas opções extras no seu cabeçalho HTTP.

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

Ao fornecer uma data no passado, ela não será armazenada em cache pelo navegador. Cache-Controlfoi adicionado no HTTP / 1.1 e a tag must-revalidate indica que os proxies nunca devem exibir uma imagem antiga, mesmo em circunstâncias atenuantes, e isso Pragma: no-cachenão é realmente necessário para os navegadores / caches modernos atuais, mas pode ajudar com algumas implementações antigas e quebradas.

Edward KMETT
fonte
3
Parecia que teria funcionado, mas ainda mostra a mesma imagem, mesmo com os hacks. Vou adicionar as informações do cabeçalho à pergunta.
QueueHammer 02/07/2009
Acabei de perceber que você está atualizando a mesma tag img repetidamente. O navegador provavelmente está detectando quando você define o src que o src não mudou e, portanto, não se incomoda em atualizar. (Como essa verificação está acontecendo no nível do DOM e não tem nada a ver com o destino). O que acontece se você adicionar "?" + número - para o URL da imagem que está sendo recuperada?
Edward KMETT 02/07/2009
3

Eu tinha um requisito: 1) não pode adicionar nenhum ?var=xxà imagem 2) deve funcionar em vários domínios

Eu realmente gosto da opção # 4 nesta resposta com uma, mas:

  • ele tem problemas para trabalhar com domínio cruzado de forma confiável (e requer tocar no código do servidor).

Minha maneira rápida e suja é:

  1. Criar iframe oculto
  2. Carregue a página atual nela (sim, a página inteira)
  3. iframe.contentWindow.location.reload(true);
  4. Redefina a fonte da imagem para si mesma

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

Sim, eu sei, setTimeout ... Você precisa mudar isso para eventos onload apropriados.

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

Então abaixo em algum 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>

E o que isso faz é que, quando a imagem é carregada, a agenda é recarregada em 1 segundo. Estou usando isso em uma página com câmeras de segurança doméstica de tipos variados.

J Fields
fonte
2

Acabei fazendo o servidor mapear qualquer solicitação de imagem nesse diretório para a fonte que eu estava tentando atualizar. Depois, meu cronômetro anexou um número ao final do nome para que o DOM o visse como uma nova imagem e o carregasse.

Por exemplo

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

solicitará o mesmo código de geração de imagens, mas parecerá imagens diferentes para o 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
fonte
8
Isso teria o mesmo problema de armazenar em cache várias cópias da imagem que a solução de querystring (Paolo e outras) e requer alterações no servidor.
18712 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
fonte
3
por favor explicar para o OP como e por que isso ajuda em vez de apenas colar código
nomistic
Eu não acho que ../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit)é um nome de arquivo válido ... Pelo menos é isso que ele tenta carregar ...
ByteHamster
mudar path='../showImage.php';parapath='../showImage.php?';
BOOMik
2
document.getElementById("img-id").src = document.getElementById("img-id").src

defina seu próprio src como seu src.

Hardik
fonte
1

Tente usar uma string de consulta sem valor para torná-la um URL exclusivo:

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
fonte
Adicionado o WQS ao código e verificado se a solicitação está sendo aceita e que o navegador vê a resposta como proveniente do endereço + WQS sem a atualização da imagem.
QueueHammer 02/07/2009
1

Altamente baseado no código # 4 de Doin, o exemplo abaixo simplifica bastante esse código, em document.writevez de src no iframesuporte ao CORS. Também se concentra apenas em bloquear o cache do navegador, e não recarregar todas as imagens na página.

Abaixo está escrito typescripte usa a biblioteca de promessa angular $ q , apenas fyi, mas deve ser fácil o suficiente para portar para javascript vanilla. O método deve viver dentro de uma classe de texto datilografado.

Retorna uma promessa que será resolvida quando o iframe concluir o recarregamento. Não é muito testado, mas funciona bem para nós.

    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;
    }
Vencedor
fonte
Para se proteger contra erros do XSS, você deve usar + encodeURI(src) +corretamente para escapar srcno iframe.
Tino
1

O código a seguir é útil para atualizar a imagem quando um botão é clicado.

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
fonte
Votado. Como é apenas uma cópia ligeiramente modificada do código do @ Mahmoud, mas em contraste isso aqui NÃO está atualizando a imagem
Tino
0

Resolvi esse problema enviando os dados de volta através de um 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());

Em seguida, na página, você fornece o servlet com alguns parâmetros para obter o arquivo de imagem correto.

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

Aqui está a minha solução. É muito simples. O agendamento de quadros poderia ser melhor.

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

Não há necessidade de new Date().getTime()travessuras. Você pode enganar o navegador tendo uma imagem fictícia invisível e usando jQuery .load () e, em seguida, criando uma nova imagem a 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
fonte
0

Solução simples: adicione este cabeçalho à resposta:

Cache-control: no-store

Por que isso funciona é explicado claramente nesta página oficial : https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

Também explica por no-cacheque não funciona.

Outras respostas não funcionam porque:

Caching.deleteé sobre um novo cache que você pode criar para trabalhos off-line, consulte: https://web.dev/cache-api-quick-guide/

Fragmentos que usam um # no URL não funcionam porque o # diz ao navegador para não enviar uma solicitação ao servidor.

Um cache-buster com uma parte aleatória adicionada ao URL funciona, mas também preencherá o cache do navegador. No meu aplicativo, eu queria baixar uma imagem de 5 MB a cada segundo de uma webcam. Vai demorar apenas uma hora ou menos para congelar completamente o seu PC. Ainda não sei por que o cache do navegador não se limita a um máximo razoável, mas isso é definitivamente uma desvantagem.

Roland
fonte
0

Melhorei o script do AlexMA para mostrar minha webcam em uma página da web que periodicamente carrega uma nova imagem com o mesmo nome. Eu tive problemas que às vezes a imagem tremia por causa de uma imagem quebrada ou por uma imagem carregada (incompleta). Para evitar oscilações, verifico a altura natural da imagem porque o tamanho da imagem da minha webcam não mudou. Somente se a altura da imagem carregada corresponder à altura da imagem original, a imagem completa será mostrada na 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
fonte
-3

Eu usei o conceito abaixo de primeiro vincular a imagem com um URL falso (buffer) e depois vinculá-lo ao URL válido.

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

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

Dessa forma, estou forçando o navegador a atualizar com um URL válido.

Prashanth Shivasubramani
fonte