Segarkan gambar dengan yang baru di url yang sama


333

Saya mengakses tautan di situs saya yang akan memberikan gambar baru setiap kali diakses.

Masalah yang saya hadapi adalah bahwa jika saya mencoba memuat gambar di latar belakang dan kemudian memperbarui gambar di halaman, gambar tidak berubah - meskipun diperbarui ketika saya memuat ulang halaman.

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

Header sebagaimana FireFox melihatnya:

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

Saya perlu memaksakan penyegaran hanya pada gambar itu di halaman. Ada ide?


Jawaban:


351

Coba tambahkan pembuka obrolan di akhir url:

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

Ini akan menambahkan cap waktu saat ini secara otomatis ketika Anda membuat gambar, dan itu akan membuat browser mencari lagi untuk gambar alih-alih mengambil yang ada di cache.


26
Bukan solusi yang sangat baik karena akan membanjiri cache (baik lokal maupun hulu). Jawaban Aya memiliki cara yang lebih baik dalam menghadapi ini.
Tgr

1
Juga, menampilkan kembali gambar yang sama di tempat lain, tanpa "cache breaker" nanti, masih menunjukkan versi cache lama (setidaknya di firefox) keduanya? dan # :(
T4NK3R

3
Anda dapat membuat lebih sedikit kode dengan ini:'image.jpg?' + (+new Date())
Lev Lukomsky

4
Ada Date.now()untuk ini
vp_arth

2
Mengapa tidakMath.random()
Gowtham Gopalakrishnan

227

Saya telah melihat banyak variasi dalam jawaban untuk bagaimana melakukan ini, jadi saya pikir saya akan meringkasnya di sini (ditambah menambahkan metode ke-4 dari penemuan saya sendiri):


(1) Tambahkan parameter kueri penghilang cache yang unik ke URL, seperti:

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

Kelebihan: 100% andal, cepat & mudah dipahami dan diimplementasikan.

Cons: Bypass caching sama sekali, berarti penundaan yang tidak perlu dan penggunaan bandwidth setiap kali gambar tidak berubah di antara tampilan. Akan berpotensi mengisi cache browser (dan cache perantara) dengan banyak, banyak salinan gambar yang persis sama! Juga, membutuhkan modifikasi URL gambar.

Kapan harus digunakan: Gunakan saat gambar terus berubah, seperti untuk umpan webcam langsung. Jika Anda menggunakan metode ini, pastikan untuk menyajikan gambar itu sendiri dengan Cache-control: no-cacheheader HTTP !!! (Seringkali ini dapat diatur menggunakan file .htaccess). Kalau tidak, Anda akan secara progresif mengisi cache dengan versi gambar yang lama!


(2) Tambahkan parameter kueri ke URL yang hanya berubah ketika file melakukannya, misalnya:

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

(Itu kode sisi server PHP, tetapi poin penting di sini adalah hanya querystring ? M = [file last-modified] ditambahkan ke nama file).

Kelebihan: 100% andal, cepat & mudah dipahami dan diimplementasikan, dan menjaga keunggulan caching dengan sempurna.

Cons: Membutuhkan memodifikasi URL gambar. Juga, sedikit lebih banyak pekerjaan untuk server - itu harus mendapatkan akses ke waktu file-terakhir diubah. Juga, memerlukan informasi sisi server, jadi tidak cocok untuk solusi murni sisi-klien untuk memeriksa gambar yang di-refresh.

Kapan menggunakan: Ketika Anda ingin menyimpan gambar, tetapi mungkin perlu memperbaruinya di server dari waktu ke waktu tanpa mengubah nama file itu sendiri. DAN ketika Anda dapat dengan mudah memastikan bahwa querystring yang benar ditambahkan ke setiap instance gambar dalam HTML Anda.


(3) Sajikan gambar Anda dengan tajuk Cache-control: max-age=0, must-revalidate, dan tambahkan memcache -busting identifier fragmen unik ke URL, seperti:

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

Idenya di sini adalah bahwa header-kontrol cache menempatkan gambar dalam cache browser, tetapi segera menandai mereka basi, sehingga setiap kali mereka ditampilkan kembali browser harus memeriksa dengan server untuk melihat apakah mereka telah berubah. Ini memastikan bahwa cache HTTP browser selalu mengembalikan salinan gambar terbaru. Namun, browser akan sering menggunakan kembali salinan gambar dalam memori jika mereka memilikinya, dan bahkan tidak memeriksa cache HTTP mereka dalam kasus itu. Untuk mencegah hal ini, pengidentifikasi fragmen digunakan: Perbandingan di dalam memori gambar srctermasuk pengidentifikasi fragmen, tapi itu dilucuti sebelum meminta cache HTTP. (Jadi, misalnya, image.jpg#Adan image.jpg#Bmungkin keduanya ditampilkan dari image.jpgentri di cache HTTP browser, tetapiimage.jpg#Btidak akan pernah ditampilkan menggunakan data gambar yang disimpan dalam memori sejak kapan image.jpg#Aterakhir ditampilkan).

Kelebihan: Menggunakan mekanisme cache HTTP yang benar, dan menggunakan gambar cache jika tidak diubah. Bekerja untuk server yang tersedak querystring ditambahkan ke URL gambar statis (karena server tidak pernah melihat pengidentifikasi fragmen - mereka hanya untuk penggunaan browser sendiri).

Kekurangan: Bergantung pada perilaku browser yang agak meragukan (atau paling tidak didokumentasikan dengan buruk), sehubungan dengan gambar dengan pengidentifikasi fragmen di URL mereka (Namun, saya telah menguji ini dengan sukses di FF27, Chrome33, dan IE11). Apakah masih mengirim permintaan validasi ulang ke server untuk setiap tampilan gambar, yang mungkin berlebihan jika gambar hanya jarang berubah dan / atau latensi adalah masalah besar (karena Anda perlu menunggu respon validasi ulang bahkan ketika gambar yang di-cache masih bagus) . Membutuhkan modifikasi URL gambar.

Kapan harus digunakan: Gunakan ketika gambar mungkin sering berubah, atau perlu disegarkan sesekali oleh klien tanpa keterlibatan skrip sisi server, tetapi di mana Anda masih menginginkan keuntungan dari caching. Misalnya, polling webcam langsung yang memperbarui gambar secara tidak teratur setiap beberapa menit. Atau, gunakan alih-alih (1) atau (2) jika server Anda tidak mengizinkan querystrings pada URL gambar statis.


(4) Segarkan secara paksa gambar tertentu menggunakan Javascript, dengan terlebih dahulu memuatnya ke tersembunyi <iframe>dan kemudian memanggil location.reload(true)iframe contentWindow.

Langkah-langkahnya adalah:

  • Muat gambar untuk disegarkan ke dalam iframe tersembunyi. Ini hanya langkah penyiapan - bisa dilakukan jauh-jauh hari sebelum penyegaran aktual, jika diinginkan. Bahkan tidak masalah jika gambar gagal dimuat pada tahap ini!

  • Setelah selesai, kosongkan semua salinan gambar itu di laman Anda atau di mana pun di simpul DOM (bahkan di luar laman yang disimpan dalam variabel javascript). Ini diperlukan karena browser dapat menampilkan gambar dari salinan dalam-basi basi (IE11 terutama melakukan ini): Anda perlu memastikan semua salinan dalam-memori dihapus, sebelum menyegarkan cache HTTP. Jika kode javascript lain berjalan secara tidak serempak, Anda mungkin juga perlu mencegah kode itu membuat salinan baru dari gambar yang akan di-refresh sementara itu.

  • Panggil iframe.contentWindow.location.reload(true). The truepasukan bypass cache reload langsung dari server dan Timpa salinan cache yang ada.

  • Setelah selesai memuat ulang , kembalikan gambar yang kosong. Mereka sekarang harus menampilkan versi baru dari server!

Untuk gambar dengan domain yang sama, Anda dapat memuat gambar ke iframe secara langsung. Untuk gambar lintas-domain, Anda harus memuat halaman HTML dari domain Anda yang berisi gambar dalam <img>tag, jika tidak, Anda akan mendapatkan kesalahan "Akses Ditolak" ketika mencoba menelepon iframe.contentWindow.reload(...).

Pro: Berfungsi persis seperti fungsi image.reload () yang Anda inginkan dimiliki DOM! Mengizinkan gambar di-cache secara normal (bahkan dengan tanggal kedaluwarsa di masa mendatang jika Anda menginginkannya, sehingga menghindari revalidasi yang sering dilakukan). Memungkinkan Anda menyegarkan gambar tertentu tanpa mengubah URL untuk gambar itu di halaman saat ini, atau di halaman lain, hanya menggunakan kode sisi klien.

Cons: Bergantung pada Javascript. Tidak 100% dijamin berfungsi dengan baik di setiap browser (saya sudah mencoba ini dengan sukses di FF27, Chrome33, dan IE11). Sangat rumit dibandingkan metode lainnya.

Kapan harus digunakan: Ketika Anda memiliki koleksi gambar yang pada dasarnya statis yang ingin Anda cache, tetapi Anda masih harus dapat memperbaruinya sesekali dan mendapatkan umpan balik visual segera bahwa pembaruan itu terjadi. (Terutama ketika me-refresh seluruh halaman browser tidak akan berfungsi, seperti pada beberapa aplikasi web yang dibangun di AJAX misalnya). Dan ketika metode (1) - (3) tidak layak karena (untuk alasan apa pun) Anda tidak dapat mengubah semua URL yang mungkin berpotensi menampilkan gambar yang harus Anda perbarui. (Perhatikan bahwa menggunakan 3 metode itu gambar akan di-refresh, tetapi jika halaman lain kemudian mencoba untuk menampilkan gambar itu tanpa querystring atau pengidentifikasi fragmen yang tepat, itu mungkin akan menunjukkan versi yang lebih lama).

Rincian penerapan ini dalam peri yang kuat dan fleksibel diberikan di bawah ini:

Anggap situs web Anda mengandung 1x1 piksel kosong .gif di jalur URL /img/1x1blank.gif, dan juga memiliki skrip PHP satu baris berikut (hanya diperlukan untuk menerapkan penyegaran paksa ke gambar lintas-domain , dan dapat ditulis ulang dalam bahasa skrip sisi server apa pun , tentu saja) di jalur URL /echoimg.php:

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

Kemudian, inilah implementasi realistis tentang bagaimana Anda dapat melakukan semua ini dalam Javascript. Ini terlihat agak rumit, tetapi ada banyak komentar, dan fungsi yang penting adalah forceImgReload () - dua yang pertama hanya gambar kosong dan tidak kosong, dan harus dirancang untuk bekerja secara efisien dengan HTML Anda sendiri, jadi beri kode sebagai terbaik untuk Anda; banyak komplikasi di dalamnya mungkin tidak perlu untuk situs web Anda:

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

Kemudian, untuk memaksa penyegaran gambar yang terletak di domain yang sama dengan halaman Anda, Anda bisa melakukan:

forceImgReload("myimage.jpg");

Untuk menyegarkan gambar dari tempat lain (lintas-domain):

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

Aplikasi yang lebih maju mungkin memuat ulang gambar setelah mengunggah versi baru ke server Anda, menyiapkan tahap awal proses memuat ulang secara bersamaan dengan mengunggah, untuk meminimalkan keterlambatan pemuatan yang terlihat oleh pengguna. Jika Anda melakukan unggahan melalui AJAX, dan server mengembalikan array JSON yang sangat sederhana [sukses, lebar, tinggi] maka kode Anda mungkin terlihat seperti ini:

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

Catatan terakhir: Meskipun topik ini membahas tentang gambar, topik ini juga berpotensi berlaku untuk jenis file atau sumber lainnya. Misalnya, mencegah penggunaan skrip basi atau file css, atau bahkan mungkin menyegarkan dokumen PDF yang diperbarui (menggunakan (4) hanya jika diatur untuk membuka di-browser). Metode (4) mungkin memerlukan beberapa perubahan pada javascript di atas, dalam kasus ini.


Saya suka ide metode 4, tetapi Anda tidak dapat memuat konten eksternal dengan Iframe, bukan? Saat ini saya menggunakan metode 3 dalam aplikasi web satu halaman, tapi saya tidak suka kenyataan bahwa Anda harus memuat ulang seluruh halaman untuk mendapatkan gambar baru, bahkan jika HTML templat dimuat ulang.
Emilios1995

@Emilios: ... Juga, saya tidak mengerti komentar Anda tentang keharusan memuat ulang seluruh halaman. Kedua metode (3) dan (4) dapat diimplementasikan dalam javascript sisi klien, tanpa memuat ulang apa pun kecuali satu gambar yang sedang disegarkan. Untuk metode (3) itu berarti menggunakan javascript untuk mengubah properti 'src' gambar Anda dari (misalnya) image.jpg#123menjadi image.jpg#124(atau apa pun, asalkan bit setelah perubahan '#'). Bisakah Anda mengklarifikasi apa yang Anda reload, dan mengapa?
Lakukan

@Emilios: Anda memang dapat memuat konten eksternal (lintas domain) ke iframe ... tetapi kemudian Anda tidak dapat mengaksesnya contentWindowmelalui javascript untuk melakukan reload(true)panggilan yang merupakan bagian penting dari metode ... jadi tidak, metode (4) ) tidak akan berfungsi untuk konten lintas domain. Terlihat dengan baik; Saya akan memperbarui "Kontra" untuk memasukkan itu.
Lakukan

@Emilios: Ups, tidak, saya tidak akan: Saya menyadari bahwa perbaikan sederhana (sekarang termasuk dalam jawaban saya) memungkinkannya berfungsi untuk gambar lintas domain juga (asalkan Anda dapat menempatkan skrip sisi server di server Anda).
Lakukan

@ pseudosavant: Sayangnya saya memperhatikan ini hanya ~ 17 bulan kemudian, tapi saya menyesal harus memberi tahu Anda, hasil edit Anda terhadap kode saya rusak parah. (Agar adil, saya pikir kode panggilan balik yang saya miliki awalnya juga tidak benar). Sekarang saya telah menulis ulang bagian (4) secara ekstensif, baik penjelasan & kode. Kode Anda sebelumnya tidak pernah kosong dalam gambar (sehingga bisa gagal dengan cara yang aneh, terutama di IE dan terutama jika gambar itu ditampilkan di beberapa tempat), tetapi lebih buruk itu juga menghapus iframe segera setelah memulai isi ulang penuh, yang mungkin berarti itu akan hanya bekerja sesekali atau tidak sama sekali. Maaf!
Lakukan

185

Sebagai alternatif untuk ...

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

...tampaknya...

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

... cukup untuk mengelabui cache browser tanpa melewati cache upstream apa pun, dengan asumsi Anda mengembalikan Cache-Controlheader yang benar . Meskipun Anda dapat menggunakan ...

Cache-Control: no-cache, must-revalidate

... Anda kehilangan manfaat dari header If-Modified-Sinceatau If-None-Match, jadi sesuatu seperti ...

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

... harus mencegah browser mengunduh ulang seluruh gambar jika belum benar-benar berubah. Diuji dan bekerja pada IE, Firefox, dan Chrome. Mengganggu gagal di Safari kecuali jika Anda menggunakan ...

Cache-Control: no-store

... meskipun ini masih lebih baik daripada mengisi cache upstream dengan ratusan gambar yang identik, terutama ketika mereka berjalan di server Anda sendiri. ;-)

Pembaruan (2014-09-28): Saat ini sepertinya Cache-Control: no-storediperlukan juga untuk Chrome.


1
Bagus! Setelah banyak waktu mencoba memuat gambar web saya yang sedang dimuat dengan penundaan saya baru saja menyelesaikannya dengan menerapkan solusi Anda (dengan '#', menggunakan '?' Tidak bekerja untuk saya). Terimakasih banyak!!!
user304602

18
Ada DUA cache yang terlibat di sini: Ada cache HTTP reguler browser, dan cache gambar di memori yang baru saja ditampilkan. Cache dalam memori yang terakhir ini diindeks oleh srcatribut lengkap , jadi menambahkan pengenal fragmen yang unik memastikan bahwa gambar tidak hanya ditarik dari memori. Tetapi pengidentifikasi fragmen tidak dikirim sebagai bagian dari permintaan HTTP, sehingga cache HTTP biasa akan digunakan seperti biasa. Itu sebabnya teknik ini berhasil.
Doin

Ada beberapa cache header. sebenarnya saya tidak tahu bahasa Inggris dengan baik, bisakah Anda memberi tahu saya haruskah saya menggunakan yang mana ?! Saya ingin sesuatu yang tidak hanya cache foto yang diubah (seperti captcha), dan cache hal lain. sehingga Cache-Control: max-age=0, must-revalidatebaik untuk saya?
Shafizadeh

Itu tidak bekerja untuk saya. Satu-satunya hal yang berbeda dalam kasus saya adalah bahwa saya memiliki url ke tindakan pengontrol yang mengambil img dari db. Saya punya argumen lain untuk aksi pengontrol jadi saya menambahkannya sebagai "...... & convert = true & t =" + Date baru (). GetTime (); dan "...... & convert = true #" + Date baru (). getTime () ;. Adakah yang saya lakukan salah?
shaffooo

1
Untuk menghindari overhead pembuatan objek dan / atau pemanggilan metode, Anda bisa menggunakan bilangan bulat yang bertambah sebagai cache-buster:newImage.src = "http://localhost/image.jpg#" + i++;
laindir

7

Setelah membuat gambar baru, apakah Anda menghapus gambar lama dari DOM dan menggantinya dengan yang baru?

Anda bisa mengambil gambar baru setiap panggilan updateImage, tetapi tidak menambahkannya ke halaman.

Ada sejumlah cara untuk melakukannya. Sesuatu seperti ini akan berhasil.

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

Setelah berhasil, jika masih ada masalah, mungkin masalah caching seperti yang dibicarakan jawaban lain.


3

Salah satu jawabannya adalah menambahkan beberapa parameter permintaan kueri seperti yang disarankan.

Jawaban yang lebih baik adalah memancarkan beberapa opsi tambahan di header HTTP Anda.

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

Dengan memberikan tanggal di masa lalu, itu tidak akan di-cache oleh browser. Cache-Controlditambahkan dalam HTTP / 1.1 dan tag yang harus divalidasi ulang menunjukkan bahwa proksi tidak boleh menampilkan gambar lama bahkan dalam keadaan meringankan, dan yang Pragma: no-cachetidak benar-benar diperlukan untuk browser / cache modern saat ini tetapi dapat membantu dengan beberapa implementasi lama yang rusak.


3
Ini terdengar seperti itu akan bekerja tetapi masih menunjukkan gambar yang sama, bahkan dengan peretasan. Saya akan menambahkan informasi header ke pertanyaan.
QueueHammer

Saya baru saja memperhatikan bahwa Anda menyegarkan tag img yang sama berulang kali. Browser mungkin mendeteksi ketika Anda pergi untuk mengatur src bahwa src belum berubah, dan itu tidak mengganggu untuk menyegarkan. (Karena pemeriksaan ini terjadi pada tingkat DOM dan tidak ada hubungannya dengan target). Apa yang terjadi jika Anda menambahkan "?" + angka - ke url gambar yang diambil?
Edward KMETT

3

Saya memiliki persyaratan: 1) tidak dapat menambahkan apapun ?var=xxke gambar 2) itu harus bekerja lintas domain

Saya sangat suka opsi # 4 dalam jawaban ini dengan satu tetapi:

  • itu memiliki masalah bekerja dengan crossdomain andal (dan itu memerlukan menyentuh kode server).

Cara cepat dan kotor saya adalah:

  1. Buat iframe tersembunyi
  2. Muat halaman saat ini ke sana (ya seluruh halaman)
  3. iframe.contentWindow.location.reload(true);
  4. Atur ulang sumber gambar ke dirinya sendiri

Ini dia

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

Ya, saya tahu, setTimeout ... Anda harus mengubahnya untuk melakukan onload-events yang tepat.


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

Kemudian di bawah ini di beberapa 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>

Jadi yang dilakukan adalah, ketika gambar dimuat, menjadwalkannya untuk dimuat ulang dalam 1 detik. Saya menggunakan ini pada halaman dengan kamera keamanan rumah dari berbagai jenis.


2

Apa yang akhirnya saya lakukan adalah meminta server memetakan permintaan gambar pada direktori itu ke sumber yang saya coba perbarui. Saya kemudian memiliki timer saya menambahkan nomor pada akhir nama sehingga DOM akan melihatnya sebagai gambar baru dan memuatnya.

Misalnya

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

akan meminta kode pembuatan gambar yang sama tetapi akan terlihat seperti gambar berbeda untuk browser.

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
Ini akan memiliki masalah yang sama dalam cache beberapa salinan gambar sebagai solusi querystring (Paolo dan beberapa yang lain), dan memerlukan perubahan server.
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
tolong jelaskan kepada OP bagaimana dan mengapa ini membantu alih-alih hanya menempelkan kode
nomistik

Saya tidak berpikir itu ../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit)adalah nama file yang valid ... Setidaknya ini yang mencoba memuat ...
ByteHamster

ubah path='../showImage.php';kepath='../showImage.php?';
BOOMik

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

atur src sendiri sebagai src-nya.


1

Coba gunakan querystring yang tidak berguna untuk menjadikannya url unik:

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

Menambahkan WQS ke kode dan memverifikasi bahwa permintaan diterima dan bahwa browser melihat respons berasal dari alamat + WQS tanpa menyegarkan gambar.
QueueHammer

1

Sangat didasarkan pada Doin ini # 4 kode, contoh berikut ini menyederhanakan kode sedikit besar memanfaatkan document.writebukannya src di iframeuntuk mendukung CORS. Juga hanya berfokus pada penghilang cache browser, tidak memuat ulang setiap gambar pada halaman.

Di bawah ini ditulis typescriptdan menggunakan perpustakaan janji angular $ q , hanya fyi, tetapi harus cukup mudah untuk port ke vanilla javascript. Metode dimaksudkan untuk hidup di dalam kelas naskah.

Mengembalikan janji yang akan diselesaikan ketika iframe telah selesai memuat ulang. Tidak banyak diuji, tetapi bekerja dengan baik untuk kita.

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

Untuk melindungi terhadap XSS bug Anda harus menggunakan + encodeURI(src) +untuk benar melarikan diri srcdi iframe.
Tino

1

Kode berikut berguna untuk menyegarkan gambar ketika tombol diklik.

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

Diturunkan. Karena ini hanya salinan kode @ Mahmoud yang sedikit dimodifikasi, tetapi sebaliknya ini di sini TIDAK menyegarkan gambar
Tino

0

Saya memecahkan masalah ini dengan mengirim data kembali melalui 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());

Kemudian dari halaman Anda hanya memberikan servlet dengan beberapa params untuk mengambil file gambar yang benar.

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

0

Inilah solusi saya. Ini sangat sederhana. Penjadwalan bingkai bisa lebih baik.

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

Tidak perlu new Date().getTime()shenanigans. Anda dapat mengelabui browser dengan memiliki gambar dummy yang tidak terlihat dan menggunakan jQuery .load (), lalu membuat gambar baru setiap kali:

<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

Solusi sederhana: tambahkan tajuk ini ke respons:

Cache-control: no-store

Mengapa ini bekerja dengan jelas dijelaskan di halaman otoritatif ini: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

Itu juga menjelaskan mengapa no-cachetidak bekerja.

Jawaban lain tidak berfungsi karena:

Caching.deleteadalah tentang cache baru yang dapat Anda buat untuk pekerjaan offline, lihat: https://web.dev/cache-api-quick-guide/

Fragmen yang menggunakan # di URL tidak berfungsi karena # memberi tahu browser untuk tidak mengirim permintaan ke server.

Cache-buster dengan bagian acak yang ditambahkan ke url berfungsi, tetapi juga akan mengisi cache browser. Di aplikasi saya, saya ingin mengunduh gambar 5 MB setiap beberapa detik dari web cam. Ini akan memakan waktu hanya satu jam atau kurang untuk sepenuhnya membekukan komputer Anda. Saya masih tidak tahu mengapa cache browser tidak terbatas pada maks yang masuk akal, tetapi ini jelas merupakan kerugian.


0

Saya meningkatkan skrip dari AlexMA untuk menunjukkan webcam saya di halaman web yang secara berkala mengunggah gambar baru dengan nama yang sama. Saya mengalami masalah yang terkadang gambarnya berkedip-kedip karena gambar yang rusak atau tidak lengkapnya gambar yang dimuat. Untuk mencegah kerlipan, saya memeriksa ketinggian alami gambar karena ukuran gambar webcam saya tidak berubah. Hanya jika tinggi gambar yang dimuat sesuai dengan tinggi gambar asli, gambar penuh akan ditampilkan di halaman.

  <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

Saya menggunakan konsep di bawah ini pertama-tama mengikat gambar dengan url (buffer) palsu dan selanjutnya mengikatnya dengan url yang valid.

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

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

Dengan cara ini, saya memaksa browser untuk menyegarkan dengan url yang valid.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.