이 작업을 수행하는 방법에 대한 답변에서 많은 변형을 보았으므로 여기에 요약 할 것이라고 생각했습니다 (내 자신의 발명품의 네 번째 방법을 추가하십시오).
(1) 고유 한 캐시 버스 팅 쿼리 매개 변수를 URL에 다음과 같이 추가하십시오.
newImage.src = "image.jpg?t=" + new Date().getTime();
장점 : 100 % 신뢰성, 빠르고 이해하기 쉽고 구현하기
단점 : 캐싱을 모두 우회하므로 이미지 가 뷰간에 변경 되지 않을 때마다 불필요한 지연 및 대역폭 사용을 의미 합니다. 정확히 동일한 이미지의 많은 사본으로 브라우저 캐시 및 중간 캐시를 채울 수 있습니다! 또한 이미지 URL을 수정해야합니다.
사용시기 : 라이브 웹캠 피드와 같이 이미지가 계속 변경 될 때 사용합니다. 이 방법을 사용하는 경우 HTTP 헤더로 이미지 자체를 제공해야합니다 Cache-control: no-cache
!!! (종종 .htaccess 파일을 사용하여 설정할 수 있습니다). 그렇지 않으면 이전 버전의 이미지로 캐시를 점진적으로 채 웁니다!
(2) 파일이 변경 될 때만 변경되는 URL에 쿼리 매개 변수를 추가합니다 (예 :
echo '<img src="image.jpg?m=' . filemtime('image.jpg') . '">';
(PHP 서버 측 코드이지만 여기서 중요한 점은? m = [file last-modified time] 쿼리 문자열이 파일 이름에 추가된다는 것입니다).
장점 : 100 % 신뢰성, 빠르고 이해하기 쉽고 구현 및 캐싱 이점을 완벽하게 유지합니다.
단점 : 이미지 URL을 수정해야합니다. 또한 서버에 대한 약간의 작업-파일 마지막 수정 시간에 액세스해야합니다. 또한 서버 측 정보가 필요하므로 새로 고친 이미지를 확인하기위한 순수한 클라이언트 측 솔루션에는 적합하지 않습니다.
사용시기 : 이미지를 캐시하고 싶지만 파일 이름 자체를 변경하지 않고 때때로 서버 끝에서 이미지를 업데이트해야 할 수 있습니다. 그리고 올바른 쿼리 문자열이 HTML의 모든 이미지 인스턴스에 추가되는지 쉽게 확인할 수 있습니다.
(3) 헤더를 사용하여 이미지를 제공 Cache-control: max-age=0, must-revalidate
하고 고유 한 memcache -busting 조각 식별자를 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을 수정해야합니다.
사용시기 : 이미지가 자주 변경되거나 서버 측 스크립트를 사용하지 않고 클라이언트가 간헐적으로 새로 고쳐야하지만 캐싱의 이점이 필요한 경우에 사용합니다. 예를 들어, 몇 분마다 이미지가 불규칙적으로 업데이트되는 라이브 웹캠을 폴링합니다. 또는 서버가 정적 이미지 URL에서 쿼리 문자열을 허용하지 않는 경우 (1) 또는 (2) 대신 사용하십시오.
(4) 숨겨진 이미지를 먼저로드 <iframe>
한 다음 location.reload(true)
iframe 을 호출 하여 Javascript를 사용하여 특정 이미지를 강제로 새로 고칩니다 contentWindow
.
단계는 다음과 같습니다.
숨겨진 iframe에 새로 고칠 이미지를로드하십시오. 이것은 설정 단계 일뿐입니다. 원하는 경우 실제 새로 고침을 오래 전에 수행 할 수 있습니다. 이 단계에서 이미지가로드되지 않더라도 중요하지 않습니다!
이 작업이 완료되면 페이지 또는 DOM 노드의 모든 위치 (자바 스크립트 변수에 저장된 페이지 외부 페이지에서도)에서 해당 이미지의 모든 사본을 비 웁니다. 브라우저가 오래된 메모리 내 사본의 이미지를 다르게 표시 할 수 있기 때문에이 작업이 필요합니다 (IE11이 특히 그러합니다) : HTTP 캐시를 새로 고치기 전에 모든 메모리 내 사본이 지워 졌는지 확인해야 합니다. 다른 자바 스크립트 코드가 비동기 적으로 실행중인 경우 해당 코드가 새로 고침 될 이미지의 새 사본을 작성하지 못하게 할 수도 있습니다.
전화하십시오 iframe.contentWindow.location.reload(true)
. true
힘 캐시 우회 서버에서 직접 다시로드하고 기존의 캐시 된 복사본을 덮어 씁니다.
다시로드 가 완료되면 빈 이미지를 복원하십시오. 이제 서버에서 최신 버전을 표시해야합니다!
동일한 도메인 이미지의 경우 이미지를 iframe에 직접로드 할 수 있습니다. 도메인 간 이미지의 경우 대신 이미지를 태그 에 포함 하는 HTML 페이지 를 도메인에서 로드 해야합니다<img>
. 그렇지 않으면을 호출 할 때 "액세스 거부"오류가 발생합니다 iframe.contentWindow.reload(...)
.
장점 : DOM에 원하는 image.reload () 함수와 동일하게 작동합니다 ! 이미지를 정상적으로 캐시 할 수 있습니다 (원하는 경우 미래의 만료일이 있더라도 재확인을 자주하지 않아도 됨). 클라이언트 쪽 코드 만 사용하여 현재 페이지 또는 다른 페이지에서 해당 이미지의 URL을 변경하지 않고 특정 이미지를 새로 고칠 수 있습니다.
단점 : 자바 스크립트를 사용합니다. 모든 브라우저에서 100 % 제대로 작동하는 것은 아닙니다 (FF27, Chrome33 및 IE11에서 성공적으로 테스트했습니다). 다른 방법에 비해 매우 복잡합니다.
사용시기 : 캐시하려는 기본적으로 정적 이미지 모음이 있지만 여전히 업데이트 할 수 있어야하며 업데이트가 발생한 즉시 시각적 인 피드백을 받아야합니다. (특히 AJAX를 기반으로 구축 된 일부 웹 앱에서와 같이 전체 브라우저 페이지를 새로 고치면 작동하지 않습니다.) 그리고 어떤 이유로 든 메소드 (1)-(3)을 실행할 수없는 경우 업데이트해야 할 이미지를 표시 할 수있는 모든 URL을 변경할 수 없습니다. (이 3 가지 방법을 사용하면 이미지가 새로 고쳐 지지만 다른 페이지 에서 적절한 쿼리 문자열이나 조각 식별자 없이 해당 이미지를 표시하려고 하면 이전 버전이 표시 될 수 있습니다).
공정하고 견고하고 유연한 방법으로이를 구현하는 세부 사항은 다음과 같습니다.
웹 사이트에 URL 경로에 빈 1x1 픽셀 .gif /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)에서 위의 자바 스크립트를 일부 변경해야 할 수도 있습니다.