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-cache
header 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 src
termasuk pengidentifikasi fragmen, tapi itu dilucuti sebelum meminta cache HTTP. (Jadi, misalnya, image.jpg#A
dan image.jpg#B
mungkin keduanya ditampilkan dari image.jpg
entri di cache HTTP browser, tetapiimage.jpg#B
tidak akan pernah ditampilkan menggunakan data gambar yang disimpan dalam memori sejak kapan image.jpg#A
terakhir 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 true
pasukan 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.