ฉันเห็นการเปลี่ยนแปลงมากมายในคำตอบสำหรับวิธีการทำเช่นนี้ดังนั้นฉันคิดว่าฉันจะสรุปได้ที่นี่ (บวกเพิ่มวิธีที่ 4 ของการประดิษฐ์ของฉันเอง):
(1) เพิ่มพารามิเตอร์คิวรีป้องกันการแคชที่ไม่ซ้ำกันไปยัง URL เช่น:
newImage.src = "image.jpg?t=" + new Date().getTime();
จุดเด่น:เชื่อถือได้ 100% รวดเร็วและง่ายต่อการเข้าใจและนำไปใช้
ข้อด้อย:บายพาสการแคชทั้งหมดหมายถึงความล่าช้าที่ไม่จำเป็นและการใช้แบนด์วิดท์เมื่อใดก็ตามที่ภาพไม่เปลี่ยนแปลงระหว่างมุมมอง อาจจะเติมแคชเบราว์เซอร์ (และแคชกลาง) ด้วยภาพจำนวนมากที่เหมือนกันหลายชุด! นอกจากนี้ต้องมีการแก้ไข URL ภาพ
เวลาที่จะใช้:ใช้เมื่อภาพมีการเปลี่ยนแปลงตลอดเวลาเช่นสำหรับฟีดเว็บแคมสด หากคุณใช้วิธีนี้ตรวจสอบให้แน่ใจว่าได้แสดงภาพตัวเองด้วยCache-control: no-cache
ส่วนหัว HTTP !!! (บ่อยครั้งที่สิ่งนี้สามารถตั้งค่าได้โดยใช้ไฟล์. htaccess) ไม่เช่นนั้นคุณจะเติมแคชของรูปภาพในเวอร์ชันเก่าอย่างต่อเนื่อง!
(2) เพิ่มพารามิเตอร์การสืบค้นไปยัง URL ที่เปลี่ยนแปลงเฉพาะเมื่อไฟล์ทำเช่น:
echo '<img src="image.jpg?m=' . filemtime('image.jpg') . '">';
(นั่นคือโค้ดฝั่งเซิร์ฟเวอร์ PHP แต่จุดสำคัญตรงนี้คือ? m = [เวลาแก้ไขล่าสุดของไฟล์] การสืบค้นจะถูกต่อท้ายชื่อไฟล์)
จุดเด่น:เชื่อถือได้ 100% รวดเร็วและง่ายต่อการเข้าใจและนำไปใช้และรักษาข้อได้เปรียบในการแคชอย่างสมบูรณ์แบบ
ข้อด้อย:ต้องแก้ไข URL รูปภาพ ยิ่งไปกว่านั้นการทำงานกับเซิร์ฟเวอร์จะต้องได้รับการเข้าถึงเวลาที่แก้ไขไฟล์ครั้งสุดท้าย นอกจากนี้ต้องใช้ข้อมูลฝั่งเซิร์ฟเวอร์ดังนั้นจึงไม่เหมาะสำหรับโซลูชันฝั่งไคลเอ็นต์เท่านั้นเพื่อตรวจสอบอิมเมจที่รีเฟรช
เวลาใช้:เมื่อคุณต้องการแคชรูปภาพ แต่อาจต้องอัปเดตที่เซิร์ฟเวอร์สิ้นสุดเป็นครั้งคราวโดยไม่ต้องเปลี่ยนชื่อไฟล์เอง และเมื่อคุณสามารถมั่นใจได้ว่ามีการเพิ่มการสืบค้นที่ถูกต้องในทุก ๆ ภาพใน HTML ของคุณ
(3) แสดงรูปภาพของคุณด้วยส่วนหัวCache-control: max-age=0, must-revalidate
และเพิ่มmemcache ที่ไม่ซ้ำกัน - การระบุตัวแยกส่วนให้กับ URL เช่น:
newImage.src = "image.jpg#" + new Date().getTime();
แนวคิดในที่นี้คือส่วนหัวของการควบคุมแคชทำให้ภาพในแคชของเบราว์เซอร์ แต่จะทำเครื่องหมายว่าค้างทันทีดังนั้นทุกครั้งที่มีการแสดงเบราว์เซอร์ใหม่จะต้องตรวจสอบกับเซิร์ฟเวอร์เพื่อดูว่ามีการเปลี่ยนแปลงหรือไม่ วิธีนี้ช่วยให้มั่นใจได้ว่าแคช HTTPของเบราว์เซอร์จะส่งคืนสำเนาล่าสุดของรูปภาพเสมอ อย่างไรก็ตามเบราว์เซอร์มักจะใช้สำเนาในหน่วยความจำของรูปภาพอีกครั้งหากมีอยู่แล้วและไม่ได้ตรวจสอบแคช HTTP ในกรณีนั้น เพื่อป้องกันสิ่งนี้มีการใช้ตัวระบุชิ้นส่วน: การเปรียบเทียบภาพในหน่วยความจำsrc
ของมีตัวระบุชิ้นส่วน แต่ได้รับการปล้นก่อนที่จะสอบถามแคช HTTP (ดังนั้นเช่นimage.jpg#A
และimage.jpg#B
อาจแสดงทั้งจากimage.jpg
รายการในแคช HTTP ของเบราว์เซอร์ แต่image.jpg#B
จะไม่ถูกแสดงโดยใช้ข้อมูลรูปภาพที่เก็บไว้ในหน่วยความจำตั้งแต่เมื่อimage.jpg#A
ปรากฏครั้งล่าสุด)
ข้อดี:ใช้กลไกการแคช HTTP อย่างเหมาะสมและใช้อิมเมจแคชหากไม่มีการเปลี่ยนแปลง ใช้งานได้กับเซิร์ฟเวอร์ที่ทำให้หายใจไม่ออกในการสอบถามเพิ่มไปยัง URL ภาพคงที่ (เนื่องจากเซิร์ฟเวอร์ไม่เคยเห็นตัวระบุชิ้นส่วน - พวกเขาเพื่อการใช้งานเบราว์เซอร์ของตัวเองเท่านั้น)
ข้อด้อย:อาศัยพฤติกรรมที่ค่อนข้างน่าสงสัย (หรืออย่างน้อยคุณภาพต่ำ) ของเบราว์เซอร์เกี่ยวกับรูปภาพที่มีตัวระบุส่วนใน URL ของพวกเขา (อย่างไรก็ตามฉันได้ทดสอบสิ่งนี้ใน FF27, Chrome33 และ IE11 เรียบร้อยแล้ว) ยังคงส่งคำขอการตรวจสอบความถูกต้องไปยังเซิร์ฟเวอร์ใหม่ทุกครั้งที่ดูภาพซึ่งอาจเกินความจำเป็นหากรูปภาพมีการเปลี่ยนแปลงเพียงเล็กน้อยและ / หรือเวลาในการตอบสนองเป็นปัญหาใหญ่ (เนื่องจากคุณต้องรอการตอบสนองการตรวจสอบความถูกต้อง . ต้องมีการแก้ไข URL รูปภาพ
เวลาใช้:ใช้เมื่อรูปภาพอาจมีการเปลี่ยนแปลงบ่อยครั้งหรือจำเป็นต้องรีเฟรชเป็นระยะโดยไคลเอนต์โดยไม่มีการมีส่วนร่วมของสคริปต์ฝั่งเซิร์ฟเวอร์ แต่ที่คุณยังต้องการความได้เปรียบของการแคช ตัวอย่างเช่นการสำรวจเว็บแคมสดที่อัพเดทภาพไม่สม่ำเสมอทุกสองสามนาที หรือใช้แทน (1) หรือ (2) หากเซิร์ฟเวอร์ของคุณไม่อนุญาตให้มีการสอบถามบน URL ภาพคงที่
(4) บังคับรีเฟรชภาพโดยเฉพาะอย่างยิ่งการใช้ Javascript โดยการโหลดครั้งแรกมันเป็นที่ซ่อน<iframe>
แล้วโทรlocation.reload(true)
บน contentWindow
iframe
ขั้นตอนคือ:
โหลดภาพที่จะรีเฟรชเป็น iframe ที่ซ่อนอยู่ นี่เป็นเพียงขั้นตอนการตั้งค่า - สามารถทำได้ล่วงหน้าก่อนการรีเฟรชจริงหากต้องการ ไม่สำคัญว่ารูปภาพจะโหลดไม่ได้ในขั้นตอนนี้หรือไม่!
เมื่อทำเสร็จแล้วให้ล้างสำเนาของภาพนั้นในหน้าของคุณหรือที่ใดก็ได้ในโหนด DOM ใด ๆ (แม้จะอยู่นอกหน้าเว็บที่จัดเก็บในตัวแปรจาวาสคริปต์) สิ่งนี้จำเป็นเนื่องจากเบราว์เซอร์อาจแสดงรูปภาพจากสำเนาในหน่วยความจำค้าง (โดยเฉพาะอย่างยิ่ง IE11 ทำสิ่งนี้): คุณต้องแน่ใจว่าสำเนาทั้งหมดในหน่วยความจำถูกล้างก่อนที่จะรีเฟรชแคช HTTP หากรหัส javascript อื่น ๆ กำลังทำงานแบบอะซิงโครนัสคุณอาจต้องป้องกันไม่ให้โค้ดนั้นสร้างสำเนาของรูปภาพที่จะรีเฟรชในระหว่างนี้
โทรiframe.contentWindow.location.reload(true)
. true
บังคับบายพาสแคชโหลดโดยตรงจากเซิร์ฟเวอร์และเขียนทับสำเนาแคชที่มีอยู่
เมื่อการโหลดอีกครั้งเสร็จสิ้นให้เรียกคืนรูปภาพเปล่า ๆ ตอนนี้พวกเขาควรจะแสดงเวอร์ชั่นใหม่จากเซิร์ฟเวอร์!
สำหรับภาพโดเมนเดียวกันคุณสามารถโหลดภาพลงใน iframe ได้โดยตรง สำหรับภาพข้ามโดเมนคุณต้องแทนโหลดหน้าเว็บแบบ HTML จากโดเมนของคุณที่มีภาพใน<img>
แท็กมิฉะนั้นคุณจะได้รับ "การเข้าถึงถูกปฏิเสธข้อผิดพลาด" iframe.contentWindow.reload(...)
เมื่อพยายามที่จะเรียกร้อง
ข้อดี:ทำงานเหมือนฟังก์ชั่น image.reload () ที่คุณต้องการให้ DOM มี! อนุญาตให้ใช้รูปภาพโดยแคชตามปกติ (แม้จะมีวันหมดอายุในอนาคตหากคุณต้องการจึงหลีกเลี่ยงการตรวจสอบความถูกต้องบ่อยครั้ง) ช่วยให้คุณสามารถรีเฟรชภาพโดยไม่เปลี่ยน URL สำหรับภาพนั้นในหน้าปัจจุบันหรือในหน้าอื่น ๆ โดยใช้รหัสฝั่งไคลเอ็นต์เท่านั้น
ข้อด้อย:อาศัย Javascript ไม่รับประกัน 100% ว่าจะทำงานได้อย่างถูกต้องในทุกเบราว์เซอร์ (ฉันได้ทดสอบสิ่งนี้ใน FF27, Chrome33 และ IE11 เรียบร้อยแล้ว) ซับซ้อนมากเมื่อเทียบกับวิธีอื่น
เวลาใช้:เมื่อคุณมีคอลเลกชันของรูปภาพแบบคงที่โดยทั่วไปที่คุณต้องการเก็บไว้ แต่คุณยังคงต้องสามารถอัปเดตเป็นครั้งคราวและรับข้อเสนอแนะภาพทันทีที่การปรับปรุงเกิดขึ้น (โดยเฉพาะเมื่อรีเฟรชหน้าเบราว์เซอร์ทั้งหมดจะไม่ทำงานเหมือนในบางเว็บแอพที่สร้างบน AJAX) และเมื่อวิธีการ (1) - (3) เป็นไปไม่ได้เพราะ (ด้วยเหตุผลใดก็ตาม) คุณไม่สามารถเปลี่ยน URL ทั้งหมดที่อาจแสดงภาพที่คุณต้องการอัปเดต (โปรดทราบว่าการใช้ทั้ง 3 วิธีรูปภาพจะถูกรีเฟรช แต่หากหน้าอื่นพยายามแสดงรูปภาพนั้นโดยไม่ใช้ตัวคัดกรองหรือตัวระบุส่วนที่เหมาะสมมันอาจแสดงเวอร์ชันที่เก่ากว่าแทน)
รายละเอียดของการใช้สิ่งนี้ในลักษณะที่แข็งแกร่งและยืดหยุ่นได้รับดังต่อไปนี้:
สมมติว่าเว็บไซต์ของคุณมีพิกเซลว่างเปล่า 1x1 พิกเซล. ที่ URL พา ธ/img/1x1blank.gif
และยังมีสคริปต์ PHP แบบบรรทัดเดียวต่อไปนี้ (จำเป็นสำหรับการใช้การรีเฟรชแบบบังคับกับภาพข้ามโดเมนเท่านั้นและสามารถเขียนใหม่ในภาษาสคริปต์ฝั่งเซิร์ฟเวอร์ใดก็ได้ แน่นอน) ที่เส้นทาง URL /echoimg.php
:
<img src="<?=htmlspecialchars(@$_GET['src'],ENT_COMPAT|ENT_HTML5,'UTF-8')?>">
จากนั้นต่อไปนี้เป็นการใช้งานจริงของวิธีที่คุณสามารถทำได้ทั้งหมดใน Javascript มันดูค่อนข้างซับซ้อน แต่มีความคิดเห็นมากมายและฟังก์ชั่นที่สำคัญคือ forceImgReload () - สองภาพแรกที่ว่างเปล่าและไม่ว่างเปล่าและควรได้รับการออกแบบให้ทำงานได้อย่างมีประสิทธิภาพกับ HTML ของคุณดังนั้นเขียนโค้ดเป็น ทำงานได้ดีที่สุดสำหรับคุณ ภาวะแทรกซ้อนส่วนใหญ่ในเว็บไซต์อาจไม่จำเป็นสำหรับคุณ
// 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);
}
จากนั้นหากต้องการบังคับให้รีเฟรชรูปภาพที่อยู่ในโดเมนเดียวกับเพจของคุณคุณสามารถทำได้:
forceImgReload("myimage.jpg");
วิธีรีเฟรชภาพจากที่อื่น (ข้ามโดเมน):
forceImgReload("http://someother.server.com/someimage.jpg", true);
แอปพลิเคชันขั้นสูงอาจจะโหลดรูปภาพใหม่หลังจากอัปโหลดเวอร์ชันใหม่ไปยังเซิร์ฟเวอร์ของคุณเตรียมขั้นตอนเริ่มต้นของกระบวนการโหลดพร้อมกันกับการอัปโหลดเพื่อลดความล่าช้าในการโหลดซ้ำที่มองเห็นให้กับผู้ใช้ หากคุณทำการอัปโหลดผ่าน AJAX และเซิร์ฟเวอร์ส่งคืนอาร์เรย์ JSON อย่างง่าย [ความสำเร็จความกว้างความสูง] รหัสของคุณอาจมีลักษณะดังนี้:
// 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));
}
หมายเหตุสุดท้าย: แม้ว่าหัวข้อนี้จะเกี่ยวกับรูปภาพ แต่อาจมีผลกับไฟล์หรือทรัพยากรชนิดอื่นด้วย ตัวอย่างเช่นการป้องกันการใช้สคริปต์เก่าหรือไฟล์ css หรืออาจรีเฟรชเอกสาร PDF ที่อัปเดตแล้ว (โดยใช้ (4) เฉพาะเมื่อตั้งค่าให้เปิดในเบราว์เซอร์) วิธีการ (4) อาจต้องมีการเปลี่ยนแปลงจาวาสคริปต์ข้างต้นในกรณีเหล่านี้