Aynı URL'deki yenisiyle görüntüyü yenile


333

Sitemde her erişildiğinde yeni bir resim sağlayacak bir bağlantıya erişiyorum.

Karşılaştığım sorun, görüntüyü arka planda yüklemeye ve ardından sayfadaki görüntüyü güncellemeye çalışırsam görüntünün değişmemesi - ancak sayfayı yeniden yüklediğimde güncellenmesidir.

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

FireFox'un gördüğü başlıklar:

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

Sayfadaki görüntünün yenilenmesini zorlamam gerekiyor. Herhangi bir fikir?


Yanıtlar:


350

URL'nin sonuna bir önbellek kırıcı eklemeyi deneyin:

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

Bu, görüntüyü oluşturduğunuzda geçerli zaman damgasını otomatik olarak ekler ve önbelleğe almak yerine tarayıcının görüntüyü tekrar aramasını sağlar.


26
Çok iyi bir çözüm değil çünkü önbellekleri (hem yerel hem de yukarı akış) batacak. Aya'nın cevabı bununla başa çıkmanın daha iyi bir yoludur.
Tgr

1
Ayrıca, daha sonra "önbellek kırıcı" olmadan aynı görüntüyü başka bir yerde yeniden görüntülemek, eski önbelleğe alınmış sürümü (en azından firefox) her ikisi de hala gösterir? ve # :(
T4NK3R

3
'image.jpg?' + (+new Date())
Bununla

4
Orada Date.now()bunun için
vp_arth

2
Neden olmasınMath.random()
Gowtham Gopalakrishnan 15:17

227

Bunu nasıl yapacağınıza dair cevaplarda çok fazla değişiklik gördüm, bu yüzden onları burada özetleyeceğim (artı kendi icadımın 4. yöntemini ekleyeceğim):


(1) URL'ye benzersiz bir önbellek bozma sorgu parametresi ekleyin, örneğin:

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

Artıları: % 100 güvenilir, hızlı ve kolay anlaşılır ve uygulanabilir.

Eksileri: Önbelleği tamamen atlar, yani görüntüde gereksiz gecikmeler ve bant genişliği kullanımı vermez görünümler arasında değişir. Potansiyel olarak tarayıcı önbelleğini (ve herhangi bir ara önbelleği) tam olarak aynı görüntünün birçok kopyasını dolduracaktır! Ayrıca, resim URL'sinin değiştirilmesini gerektirir.

Ne zaman kullanılır: Canlı bir web kamerası feed'i gibi resim sürekli değişiyorsa kullanın. Bu yöntemi kullanırsanız , resimlerin kendileriyle birlikte sunulduğundan emin olun.Cache-control: no-cache HTTP üstbilgileriyle sunduğunuzdan !!! (Genellikle bu bir .htaccess dosyası kullanılarak ayarlanabilir). Aksi takdirde önbellekleri görüntünün eski sürümleriyle aşamalı olarak dolduracaksınız!


(2) URL'ye yalnızca dosya yapıldığında değişen sorgu parametresi ekleyin, örneğin:

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

(Bu PHP sunucu tarafı kodu, ama burada önemli olan sadece? M = [dosya son değiştirilme zamanı] sorgu dizesi eklenmesi).

Artıları: % 100 güvenilir, hızlı ve anlaşılması ve uygulanması kolay ve önbellekleme avantajlarını mükemmel şekilde korur.

Eksileri: Resim URL'sinin değiştirilmesini gerektirir. Ayrıca, sunucu için biraz daha çalışma - dosya son değiştirilme zamanına erişmek zorunda. Ayrıca, sunucu tarafı bilgileri gerektirir, bu nedenle yenilenmiş bir görüntü olup olmadığını kontrol etmek için yalnızca istemci tarafı çözümü için uygun değildir.

Ne zaman kullanılır: Görüntüleri önbelleğe almak istediğinizde, ancak dosya adını değiştirmeden zaman zaman sunucu sonunda güncellemeniz gerekebilir. VE HTML'nizdeki her resim örneğine doğru sorgu dizesinin eklenmesini kolayca sağlayabildiğinizde.


(3) Resimlerinizi üstbilgiyle sunun ve URL'ye Cache-control: max-age=0, must-revalidatebenzersiz bir memcache -şütücü parça tanımlayıcı ekleyin, örneğin:

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

Buradaki fikir, önbellek denetimi başlığının görüntüleri tarayıcı önbelleğine koyduğu, ancak hemen eskiyen olarak işaretlediği, böylece her yeniden görüntülendiğinde tarayıcının değiştirilip değiştirilmediğini görmek için sunucuyla kontrol etmesi gerekir. Bu, tarayıcının HTTP önbelleğinin her zaman görüntünün en son kopyasını döndürmesini sağlar. Bununla birlikte, tarayıcılar genellikle bir görüntünün bellek içi bir kopyasını varsa kullanırlar ve bu durumda HTTP önbelleklerini kontrol etmezler. Bunu önlemek için bir parça tanımlayıcısı kullanılır: Bellek içi görüntülerin karşılaştırılması srcparça tanımlayıcısını içerir, ancak HTTP önbelleğini sorgulamadan önce çıkarılır. (Yani, örneğin,image.jpg#A ve image.jpg#Bikisinden de görüntülenebilir image.jpggirişin tarayıcı HTTP önbelleği, ancakimage.jpg#Bimage.jpg#Aen son ne zamandan itibaren bellekte tutulan görüntü verileri kullanılarak görüntülenmez).

Artıları: HTTP önbellek mekanizmalarını doğru şekilde kullanır ve değişmediyse önbelleğe alınmış görüntüleri kullanır. Statik resim URL'sine eklenen bir sorgu dizesini tıkayan sunucular için çalışır (sunucular hiçbir zaman parça tanımlayıcılarını görmediğinden - yalnızca tarayıcıların kendi kullanımları içindir).

Eksileri: URL'lerinde parça tanımlayıcılarına sahip görüntülerle ilgili olarak, tarayıcıların biraz şüpheli (veya en azından kötü belgelenmiş) davranışına dayanır (Ancak, bunu başarıyla FF27, Chrome33 ve IE11'de test ettim). Her görüntü görünümü için yine de sunucuya bir yeniden doğrulama isteği gönderir; bu, görüntüler nadiren değişiyorsa ve / veya gecikme büyükse sorun teşkil eder (önbelleğe alınan görüntü hala iyi olsa bile yeniden doğrulama yanıtını beklemeniz gerektiğinden) . Resim URL'lerinin değiştirilmesini gerektirir.

Ne zaman kullanılır: Görüntülerin sık sık değişebileceği veya istemci tarafından sunucu tarafı komut dosyası katılımı olmadan, ancak önbelleğe almanın avantajını istediğiniz yerde zaman zaman yenilenmesi gerektiğinde kullanın. Örneğin, görüntüyü birkaç dakikada bir düzensiz güncelleyen canlı bir web kamerası çağırma. Alternatif olarak, sunucunuz statik resim URL'lerinde sorgu dizelerine izin vermiyorsa (1) veya (2) yerine kullanın.


(4) Zorla içine ilk yükleme bunun tarafından Javascript kullanarak belirli bir resme yenilemek gizli <iframe>ve sonra çağıran location.reload(true)iframe en üstünde contentWindow.

Adımlar:

  • Yenilenecek resmi gizli bir iframe'e yükleyin. Bu sadece bir kurulum adımıdır - istenirse gerçek yenileme uzun zaman önce yapılabilir. Görüntünün bu aşamada yüklenememesi bile önemli değil!

  • Bu yapıldıktan sonra, o görüntünün sayfalarınızdaki veya herhangi bir DOM düğümündeki (javascript değişkenlerinde depolanan sayfa dışı olanlar bile) tüm kopyalarını boşaltın. Bu, tarayıcının görüntüyü eski bir bellek içi kopyadan görüntüleyebilmesi nedeniyle gereklidir (IE11 özellikle bunu yapar): HTTP önbelleğini yenilemeden önce tüm bellek içi kopyaların temizlendiğinden emin olmanız gerekir . Diğer javascript kodu eşzamansız olarak çalışıyorsa, bu kodun yenilenecek görüntünün yeni kopyalarını bu arada oluşturmasını da engellemeniz gerekebilir.

  • Arayın iframe.contentWindow.location.reload(true). trueKuvvetler bir önbellek baypas, sunucudan doğrudan yeniden ve mevcut önbelleğe alınmış bir kopyasını üzerine yazarak.

  • Yeniden yükleme işlemi bittikten sonra , boş görüntüleri geri yükleyin. Şimdi sunucudan yeni sürümü görüntülemelidir!

Aynı etki alanı görüntüleri için, görüntüyü doğrudan iframe'e yükleyebilirsiniz. Alanlar arası resimler için bunun yerine , alan adınızdan bir <img>etikette resim içeren bir HTML sayfası yüklemeniz gerekir , aksi takdirde aramaya çalıştığınızda "Erişim Reddedildi" hatası alırsınız iframe.contentWindow.reload(...).

Artıları: Tıpkı image.reload () işlevi gibi çalışır olmasını istediğiniz ! Görüntülerin normal olarak önbelleğe alınmasına izin verir (isterseniz son kullanma tarihlerinde bile, böylece sık sık yeniden doğrulama yapılmasını önler). Yalnızca istemci tarafı kodunu kullanarak geçerli sayfadaki veya diğer sayfalardaki URL'leri değiştirmeden belirli bir görüntüyü yenilemenize olanak tanır.

Eksileri: Javascript dayanır. Her tarayıcıda% 100 düzgün çalışması garanti edilmez (Bunu FF27, Chrome33 ve IE11'de başarıyla test ettim). Diğer yöntemlere göre çok karmaşık.

Ne zaman kullanılır: Önbelleğe alınmasını istediğiniz temelde statik resimlerden oluşan bir koleksiyona sahip olduğunuzda, ancak bunları arada sırada güncelleyebilmeniz ve güncellemenin gerçekleştiği anında görsel geri bildirim alabilmeniz gerekir. (Özellikle AJAX üzerine kurulmuş bazı web uygulamalarında olduğu gibi, tarayıcı sayfasının tamamını yenilerken çalışmaz). Ve (1) - (3) yöntemleri uygun olmadığında (ne olursa olsun), güncellemeniz gereken resmi gösterebilecek potansiyel URL'leri değiştiremezsiniz. (Bu 3 yöntemi kullanarak görüntünün yenileneceğini unutmayın, ancak başka bir sayfa bu görüntüyü uygun sorgu dizesi veya parça tanımlayıcısı olmadan görüntülemeye çalışırsa , bunun yerine eski bir sürüm gösterebilir).

Bunu bir peri sağlam ve esnek bir şekilde uygulamanın detayları aşağıda verilmiştir:

Web sitenizin URL yolunda boş bir 1x1 piksel .gif içerdiğini /img/1x1blank.gifve ayrıca aşağıdaki tek satırlı PHP komut dosyasına sahip olduğunu varsayalım (yalnızca etki alanları arası görüntülere zorunlu yenileme uygulamak için gereklidir ve herhangi bir sunucu tarafı komut dosyası dilinde yeniden yazılabilir) , elbette) URL yolunda /echoimg.php:

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

Ardından, tüm bunları Javascript'te nasıl yapabileceğinizin gerçekçi bir uygulaması. Biraz karmaşık görünüyor, ancak çok fazla yorum var ve önemli işlev sadece forceImgReload () - ilk ikisi sadece boş ve boş olmayan görüntüler ve kendi HTML'nizle verimli çalışacak şekilde tasarlanmalıdır, bu yüzden bunları kodlayın sizin için en iyi şekilde çalışır; bunlardaki komplikasyonların çoğu web siteniz için gereksiz olabilir:

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

Ardından, sayfanızla aynı alanda bulunan bir görüntünün yenilenmesini zorlamak için şunları yapabilirsiniz:

forceImgReload("myimage.jpg");

Görüntüyü başka bir yerden yenilemek için (web alanları arası):

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

Daha gelişmiş bir uygulama, sunucunuza yeni bir sürüm yükledikten sonra görüntüyü yeniden yüklemek ve kullanıcıya yeniden yükleme gecikmesini en aza indirmek için yeniden yükleme işleminin başlangıç ​​aşamasını yükleme ile aynı anda hazırlamak olabilir. AJAX üzerinden yükleme yapıyorsanız ve sunucu çok basit bir JSON dizisi [başarı, genişlik, yükseklik] döndürüyorsa, kodunuz şöyle görünebilir:

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

Son bir not: Bu konu görüntülerle ilgili olmasına rağmen, potansiyel olarak diğer dosya veya kaynaklar için de geçerlidir. Örneğin, eski komut dosyası veya css dosyalarının kullanılmasının önlenmesi veya belki de güncellenmiş PDF belgelerinin yenilenmesi (yalnızca tarayıcıda açılacak şekilde ayarlanmışsa (4) kullanılarak). Yöntem (4), bu gibi durumlarda yukarıdaki javascript üzerinde bazı değişiklikler yapılmasını gerektirebilir.


Yöntem 4 fikrini seviyorum, ancak Iframe ile harici içerik yükleyemezsiniz, değil mi? Şu anda tek sayfalık bir web uygulamasında yöntem 3 kullanıyorum, ancak şablonun HTML'si yeniden yüklense bile yeni görüntüyü almak için tüm sayfayı yeniden yüklemeniz gerekmiyor.
Emilios1995

@Emilios: ... Ayrıca, tüm sayfayı yeniden yüklemek zorunda kaldığınız hakkındaki yorumunuzu da anlamıyorum. (3) ve (4) yöntemlerinin her ikisi de, yenilemekte olduğunuz görüntü dışında hiçbir şey yeniden yüklenmeden istemci tarafı javascript'te uygulanabilir. Yöntem (3) için bu, görüntünüzün 'src' özelliğini (örneğin) ' image.jpg#123den image.jpg#124(veya herhangi bir şeye ' # 'değiştikten sonraki bit olduğu sürece ) değiştirmek için javascript kullanmak anlamına gelir . Neyi yeniden yüklediğinizi açıklığa kavuşturabilir misiniz ve neden?
Doin

@Emilios: Gerçekten bir iframe içine harici (siteler arası) içerik yükleyebilirsiniz ... ancak daha sonra yöntemin kritik kısmı olan çağrıyı contentWindowyapmak için javascript aracılığıyla erişemezsiniz reload(true)... yani hayır, yöntem (4 ) alanlar arası içerik için çalışmaz. İyi tespit; Bunu eklemek için "Eksileri" güncelleyeceğim.
Doin

@Emilios: Hata! Hayır: Yapmayacağım: Basit bir düzeltmenin (şimdi yanıtıma dahil edildi) etki alanları arası görüntüler için de çalışmasına izin verdiğini fark ettim (sunucunuza bir sunucu tarafı komut dosyası koyabiliyorsanız).
Doin

@pseudosavant: Maalesef bunu sadece ~ 17 ay sonra fark ediyorum, ancak söylemek istediğim için üzgünüm, kodumdaki düzenlemeleriniz bozuldu. (Adil olmak gerekirse, ben de başlangıçta vardı geri arama kodu doğru olduğunu sanmıyorum). Şimdi bölüm (4), hem açıklama hem de kod yeniden yazdım. Önceki kodunuz asla görüntüleri boş bırakmadı (bu nedenle özellikle IE'de ve özellikle görüntü birden fazla yerde gösteriliyorsa garip yollarla başarısız olabilir), ancak daha da kötüsü, tam yeniden yüklemeyi başlattıktan hemen sonra iframe'i sildi; sadece aralıklı çalışın ya da hiç çalışmayın. Afedersiniz!
Doin

185

Alternatif olarak ...

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

...öyle görünüyor...

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

... doğru Cache-Controlbaşlıkları döndürdüğünüzü varsayarak, yukarı akış önbelleklerini atlamadan tarayıcı önbelleğini kandırmak için yeterlidir . Kullanabilmenize rağmen ...

Cache-Control: no-cache, must-revalidate

... üstbilgilerin If-Modified-Sinceveya If-None-Matchbaşlıkların avantajlarını kaybedersiniz ...

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

... gerçekten değişmediyse tarayıcının tüm resmi yeniden indirmesini engellemelidir. IE, Firefox ve Chrome üzerinde test edildi ve çalışıyor. Eğer kullanmak sürece rahatsız edici Safari başarısız olur ...

Cache-Control: no-store

... yine de, özellikle kendi sunucunuzda çalışırken, yukarı akış önbelleklerini yüzlerce aynı görüntüyle doldurmak tercih edilebilir. ;-)

Güncelleme (2014-09-28): Günümüzde Cache-Control: no-storeChrome için de gerekli görünüyor .


1
Harika! Bir gecikme ile yüklenen web görüntülerimi yüklemeye çalışırken çok fazla sonra çözümünüzü uygulayarak çözdüm ('#' ile, '?' Kullanarak benim için çalışmıyor). Çok teşekkürler!!!
user304602

18
Orada İKİ tarayıcının Normal HTTP önbelleği var ve görüntülerin bir bellek içi önbellek son zamanlarda sergilemiştir: Burada yer alan önbelleğe. Bu ikinci bellek içi önbellek tam srcözellik tarafından dizine eklenir , bu nedenle benzersiz bir parça tanımlayıcı eklemek görüntünün bellekten kolayca alınmamasını sağlar. Ancak parça tanımlayıcıları HTTP isteklerinin bir parçası olarak gönderilmez, bu nedenle normal HTTP önbelleği normal olarak kullanılır. Bu yüzden bu teknik işe yarıyor.
Doin

Birkaç başlık önbelleği var. aslında ingilizceyi çok iyi bilmiyorum, lütfen hangisini kullanmam gerektiğini söyleyebilir misiniz ?! Yalnızca değiştirilen fotoğrafı (captcha gibi) önbelleğe almayan ve başka şeyleri önbelleğe almayan bir şey istiyorum. böylece Cache-Control: max-age=0, must-revalidatebenim için iyidir?
Shafizadeh

Benim için işe yaramıyor. Benim durumumda farklı tek şey, db img alır bir denetleyici eylem için bir url olmasıdır. Ben denetleyici eylem için başka argümanlar vardı, bu yüzden "...... & convert = true & t =" + new Date (). GetTime (); ve "...... & convert = true #" + yeni Date (). getTime () ;. Yanlış yaptığım bir şey var mı?
shaffooo

1
Nesne oluşturma ve / veya yöntem çağrısı yükünü önlemek için, önbellek bozucu olarak artan bir tam sayı kullanabilirsiniz:newImage.src = "http://localhost/image.jpg#" + i++;
laindir

7

Yeni görüntüyü oluşturduktan sonra eski görüntüyü DOM'dan kaldırıyor ve yenisiyle değiştiriyor musunuz?

Her güncellemeGörüntü çağrısında yeni görüntüler alıyor olabilir, ancak bunları sayfaya eklemiyor olabilirsiniz.

Bunu yapmanın birkaç yolu vardır. Böyle bir şey işe yarayacaktır.

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

Bu işe başladıktan sonra, hala sorunlar varsa, muhtemelen diğer cevaplar hakkında konuşmak gibi bir önbellek sorunu.


3

Bir cevap, hackishly gibi önerilen bazı get sorgu parametresi eklemektir.

Daha iyi bir yanıt, HTTP üstbilginizde birkaç ek seçenek yayınlamaktır.

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

Geçmişte bir tarih girdiğinizde, tarayıcı tarafından önbelleğe alınmaz. Cache-ControlHTTP / 1.1'de eklenmiştir ve mutlaka yeniden doğrula etiketi, proxy'lerin söndürme koşulları altında bile eski bir görüntüye asla hizmet Pragma: no-cacheetmemesi gerektiğini ve mevcut modern tarayıcılar / önbellekler için gerçekten gerekli olmadığını, ancak bazı kaba kırık eski uygulamalarda yardımcı olabileceğini gösterir.


3
Bu işe yaramış gibi görünüyordu, ancak yine de hack'lerde bile aynı görüntüyü gösteriyor. Soruya başlık bilgisini ekleyeceğim.
QueueHammer

Sadece aynı img etiketini tekrar tekrar yenilediğinizi fark ettim. Tarayıcı, src'nin değişmediğini src'yi ayarladığınızda gittiğinizi algılıyor ve bu nedenle yenilemek için rahatsız etmiyor. (Bu kontrol DOM düzeyinde gerçekleştiğinden ve hedefle ilgisi olmadığından). "?" Eklerseniz ne olur? + sayı - alınan görüntünün URL'sine mi?
Edward KMETT

3

Bir gereksinimim vardı: 1) ?var=xxresme herhangi bir şey ekleyemiyorum 2) alanlar arası çalışmalıdır

Gerçekten bu bir cevap ile # 4 seçeneği gibi ama:

  • etki alanları ile güvenilir bir şekilde çalışma sorunları vardır (ve sunucu koduna dokunmayı gerektirir).

Hızlı ve kirli yolum:

  1. Gizli iframe oluştur
  2. Geçerli sayfayı buraya yükle (evet tüm sayfa)
  3. iframe.contentWindow.location.reload(true);
  4. Görüntü kaynağını yeniden ayarlayın

İşte burada

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

Evet, biliyorum, setTimeout ... Bunu uygun onload olaylarına değiştirmelisin.


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

Sonra aşağıda bazı javascriptlerde

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

Ve bunun yaptığı şey, görüntü yüklendiğinde, görüntüyü 1 saniyede yeniden yüklenecek şekilde zamanlar. Bunu, farklı türdeki ev güvenlik kameralarıyla bir sayfada kullanıyorum.


2

Yaptığım şey, sunucunun güncellemeye çalıştığım kaynağa bu dizindeki herhangi bir görüntü için herhangi bir isteği eşlemesini sağlamaktı. Daha sonra kronomun adının sonuna bir sayı eklemesini sağladım, böylece DOM onu yeni bir görüntü olarak görüp yükleyecekti.

Örneğin

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

aynı görüntü oluşturma kodunu isteyecektir, ancak tarayıcıya farklı görüntüler gibi görünecektir.

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

8
Bu, sorgu dizesi çözümü (Paolo ve diğerleri) ile görüntünün birden çok kopyasını önbelleğe alma sorunu ile aynıdır ve sunucu değişiklikleri gerektirir.
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')" />


3
lütfen
OP'ye

Bunun ../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit)geçerli bir dosya adı olduğunu düşünmüyorum ... En azından yüklemeye çalıştığı şey bu ...
ByteHamster

değişim path='../showImage.php';içinpath='../showImage.php?';
BOOMik

2
document.getElementById("img-id").src = document.getElementById("img-id").src

kendi src'sini src olarak belirledi.


1

Benzersiz bir URL yapmak için değersiz bir sorgu dizesi kullanmayı deneyin:

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

Koda WQS eklendi ve isteğin kabul edildiğini ve tarayıcının yanıtı, görüntü yenilemesi olmadan + WQS adresinden geldiğini gördüğü doğrulandı.
QueueHammer

1

Ağır bir şekilde Doin'in # 4 kodunu temel alan aşağıdaki örnek  , CORS'yi desteklemek için document.writeyerine kullanmaktan büyük ölçüde kodlama işlemini basitleştirir . Ayrıca, sayfadaki her görüntüyü yeniden yüklemeye değil, yalnızca tarayıcı önbelleğini bozmaya odaklanır.srciframe

Aşağıda yazılmıştır typescriptve angular $ q promise kütüphanesini kullanır , sadece fyi, ancak vanilya javascript'e bağlanacak kadar kolay olmalıdır. Yöntem bir daktilo sınıfında yaşamak içindir.

İframe yeniden yüklemeyi tamamladığında çözülecek bir söz verir. Ağır test edilmedi, ancak bizim için iyi çalışıyor.

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

Kullanmanız gereken XSS böceklere karşı korunmak için + encodeURI(src) +düzgün kaçmak için srcde iframe.
Tino

1

Aşağıdaki kod, bir düğmeye tıklandığında görüntüyü yenilemek için yararlıdır.

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')" />

Downvoted. @ Mahmoud kodunun biraz değiştirilmiş bir kopyası olduğu için, bunun aksine burada görüntüyü yenilemez
Tino

0

Verileri bir sunucu uygulaması aracılığıyla geri göndererek bu sorunu çözdüm.

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

Sonra sayfadan sadece doğru görüntü dosyasını almak için bazı params ile sunucu uygulaması verin.

<img src="YourServlet?imageFileName=imageNum1">

0

İşte benim çözümüm. Çok basit. Çerçeve zamanlaması daha iyi olabilir.

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

0

new Date().getTime()Maskaralıklara gerek yok . Görünmez bir sahte resme sahip olarak ve jQuery .load () kullanarak, ardından her seferinde yeni bir resim oluşturarak tarayıcıyı kandırabilirsiniz:

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

0

Basit çözüm: Bu başlığı yanıta ekleyin:

Cache-control: no-store

Bunun neden işe yaradığı bu yetkili sayfada açıklanmaktadır: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

Ayrıca neden no-cacheçalışmadığını da açıklar .

Diğer cevaplar işe yaramaz çünkü:

Caching.deleteçevrimdışı çalışma için oluşturabileceğiniz yeni bir önbellek hakkındadır, bkz: https://web.dev/cache-api-quick-guide/

#, Tarayıcıya sunucuya istek göndermediğini bildirdiği için URL'de # kullanan parçalar çalışmaz.

URL'ye rastgele bir kısmı eklenmiş bir önbellek bozucu çalışır, ancak tarayıcı önbelleğini de doldurur. Uygulamamda, bir web kamerasından birkaç saniyede bir 5 MB resim indirmek istedim. Tamamen pc dondurmak için sadece bir saat veya daha az sürer. Tarayıcı önbelleğinin neden makul bir maks ile sınırlı olmadığını hala bilmiyorum, ama bu kesinlikle bir dezavantaj.


0

Web kameramı periyodik olarak aynı ada sahip yeni bir resim yükleyen bir web sayfasında göstermek için AlexMA'nın komut dosyasını geliştirdim . Bazen bozuk bir görüntü veya tam (yukarı) yüklü görüntü nedeniyle görüntünün titriyor sorunları vardı. Titremeyi önlemek için web kameramın boyutu değişmediği için görüntünün doğal yüksekliğini kontrol ediyorum. Yalnızca yüklenen görüntü yüksekliği orijinal görüntü yüksekliğine uyuyorsa, tam görüntü sayfada gösterilir.

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

-3

Aşağıdaki görüntüyü ilk olarak yanlış (tampon) bir url ile bağladıktan sonra daha sonra geçerli url ile bağlayan kavramını kullandım.

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

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

Bu şekilde, tarayıcıyı geçerli URL ile yenilemeye zorluyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.