我正在访问我的网站上的一个链接,每次访问它都会提供一个新的图像。

我遇到的问题是,如果我试图在后台加载图像,然后更新页面上的图像,图像不会改变——尽管在我重新加载页面时它会更新。

var newImage = new Image();
newImage.src = "http://localhost/image.jpg";

function updateImage()
{
if(newImage.complete) {
    document.getElementById("theText").src = newImage.src;
    newImage = new Image();
    number++;
    newImage.src = "http://localhost/image/id/image.jpg?time=" + new Date();
}

    setTimeout(updateImage, 1000);
}

FireFox看到的头文件:

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

我需要强制刷新页面上的图像。什么好主意吗?


当前回答

一种解决方法是像建议的那样笨拙地添加一些get查询参数。

更好的答案是在HTTP报头中发出两个额外的选项。

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

通过提供过去的日期,它不会被浏览器缓存。缓存控制是在HTTP/1.1中添加的,必须重新验证标签表明代理永远不应该提供旧的映像,即使是在可减轻的情况下,而Pragma: no-cache对于当前的现代浏览器/缓存来说并不是真正必要的,但可能有助于一些复杂的破碎的旧实现。

其他回答

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

将自己的SRC设置为自己的SRC。

下面的代码用于在单击按钮时刷新图像。

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

我有一个要求:1)不能添加任何?var=xx的图像2)它应该跨域工作

我真的很喜欢这个答案中的第4个选项,但是:

它在可靠地跨域工作方面存在问题(并且需要修改服务器代码)。

我的捷径是:

创建隐藏iframe 加载当前页面到它(是的,整个页面) iframe.contentWindow.location.reload(真正的); 重新设置图像源为其本身

在这里

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

我知道,setTimeout。您必须将其更改为适当的onload-events。

关于如何做到这一点,我已经看到了很多不同的答案,所以我想在这里总结一下(加上我自己发明的第4种方法):


(1)在URL中添加一个唯一的缓存破坏查询参数,例如:

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

优点:100%可靠,快速,易于理解和实现。

缺点:完全绕过缓存,这意味着当图像在视图之间没有改变时,不必要的延迟和带宽使用。将潜在地填满浏览器缓存(和任何中间缓存)与许多,许多完全相同的图像的副本!另外,需要修改图像URL。

何时使用:当图像不断变化时使用,例如用于实时网络摄像头馈送。如果你使用这种方法,请确保使用缓存控制服务图像本身:无缓存HTTP头!!(通常可以使用.htaccess文件进行设置)。否则,您将逐步用旧版本的图像填充缓存!


(2)在URL中添加查询参数,该参数仅在文件更改时才更改,例如:

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

(这是PHP服务器端代码,但这里的重点是一个?m=[文件最后修改时间]查询字符串被附加到文件名)。

优点:100%可靠,快速,易于理解和实现,并完美地保留缓存优势。

缺点:需要修改图像URL。此外,服务器还需要做一些工作——它必须访问文件最后修改的时间。此外,它需要服务器端信息,因此不适合纯客户端解决方案来检查刷新映像。

何时使用:当你想缓存图像,但可能需要在服务器端不时更新它们,而不改变文件名本身。AND时,您可以轻松地确保将正确的查询字符串添加到HTML中的每个图像实例中。


(3)使用Cache-control: max-age=0,必须重新验证,并在URL中添加一个唯一的memcache-busting片段标识符,例如:

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

The idea here is that the cache-control header puts images in the browser cache, but immediately markes them stale, so that and every time they are re-displayed the browser must check with the server to see if they've changed. This ensures that the browser's HTTP cache always returns the latest copy of the image. However, browsers will often re-use an in-memory copy of an image if they have one, and not even check their HTTP cache in that case. To prevent this, a fragment identifier is used: Comparison of in-memory image src's includes the fragment identifier, but it gets stripped of before querying the HTTP cache. (So, e.g., image.jpg#A and image.jpg#B might both be displayed from the image.jpg entry in the browser's HTTP cache, but image.jpg#B would never be displayed using in-memory retained image data from when image.jpg#A was last displayed).

优点:正确使用HTTP缓存机制,并使用缓存的图像,如果他们没有改变。适用于被添加到静态图像URL的查询字符串阻塞的服务器(因为服务器永远不会看到片段标识符-它们只供浏览器自己使用)。

Cons: Relies on somewhat dubious (or at least poorly documented) behaviour of browsers, in regard to images with fragment identifiers in their URLs (However, I've tested this successfully in FF27, Chrome33, and IE11). Does still send a revalidation request to the server for every image view, which may be overkill if images only change rarely and/or latency is a big issue (since you need to wait for the revalidation response even when the cached image is still good). Requires modifying image URLs.

何时使用:当图像可能频繁更改,或者需要由客户端间歇刷新而不涉及服务器端脚本,但仍然需要缓存优势时使用。例如,轮询每隔几分钟就会不定期更新图像的实时网络摄像头。或者,如果你的服务器不允许静态图像url上的查询字符串,使用(1)或(2)代替。

[EDIT 2021:不再适用于最近的Chrome和Edge:这些浏览器中的内部memcache现在忽略片段标识符(可能是因为切换到Blink引擎?)但是请参见下面的方法(4),现在在这两种浏览器上要容易得多,所以考虑将这种方法与(4)的简化版本结合起来,以覆盖这两种浏览器]。


(4)使用Javascript强制刷新一个特定的图像,首先将它加载到一个隐藏的<iframe>,然后在iframe的contentWindow上调用location.reload(true)。

步骤如下:

Load the image to be refreshed into a hidden iframe. [EDIT 2021: For Chrome and Edge, load a HTML page with an <img> tag, not the raw image file]. This is just a setup step - it can be done long in advance the actual refresh, if desired. It doesn't even matter if the image fails to load at this stage! [EDIT 2021: This step is now unnecessary in recent Chrome and Edge]. Once that's done, blank out all copies of that image on your page(s) or anywhere in any DOM nodes (even off-page ones stored in javascript variables). This is necessary because the browser may otherwise display the image from a stale in-memory copy (IE11 especially does this): You need to ensure all in-memory copies are cleared, before refreshing the HTTP cache. If other javascript code is running asynchronously, you may also need to prevent that code from creating new copies of the to-be-refreshed image in the meantime. Call iframe.contentWindow.location.reload(true). The true forces a cache bypass, reloading directly from the server and overwriting the existing cached copy. [EDIT 2021: This step is now unnecessary in recent Chrome and Edge - on those browsers, existing images will just automatically update themselves after the previous step!] Once it's finished re-loading, restore the blanked images. They should now display the fresh version from the server!

对于同域图像,可以直接将图像加载到iframe中。[编辑2021年:不Chrome, Edge]。对于跨域图像,你必须从你的域加载一个包含<img>标记图像的HTML页面,否则当你试图调用iframe.contentWindow.reload(…)时,你会得到一个“Access Denied”错误。[为Chrome和Edge也这样做]。

优点:就像你希望DOM拥有的image.reload()函数一样!允许正常缓存图像(如果你想要它们,甚至在未来的过期日期,从而避免频繁的重新验证)。允许您仅使用客户端代码刷新特定图像,而无需更改当前页面或任何其他页面上该图像的url。

缺点:依赖Javascript。不能100%保证在每个浏览器都能正常工作(我已经在FF27、Chrome33和IE11上测试成功了)。相对于其他方法非常复杂。[编辑2021:除非你只需要最近的Chrome和Edge支持,在这种情况下,事情就简单多了]。

When to use: When you have a collection of basically static images that you'd like cached, but you still need to be able to update them occasionally and get immediate visual feedback that the update took place. (Especially when just refreshing the whole browser page wouldn't work, as in some web apps built on AJAX for example). And when methods (1)-(3) aren't feasible because (for whatever reason) you can't change all the URLs that might potentially display the image you need to have updated. (Note that using those 3 methods the image will be refreshed, but if another page then tries to displays that image without the appropriate querystring or fragment identifier, it may show an older version instead).

以一种非常健壮和灵活的方式实现这一点的细节如下:

让我们假设您的网站在URL路径/img/1x1blank.gif中包含一个空白的1x1像素。gif,并且在URL路径/echoimg.php中也有以下一行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数组[success, width, height],那么你的代码可能是这样的:

// 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)可能需要对上面的javascript进行一些更改。

简单的解决方案:在响应中添加以下报头:

Cache-control: no-store

为什么这样做是清楚地解释在这个权威的页面:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

这也解释了为什么无缓存不能工作。

其他答案并不适用,因为:

cache .delete是关于您可以为脱机工作创建的新缓存,请参阅:https://web.dev/cache-api-quick-guide/

在URL中使用#的片段不起作用,因为#告诉浏览器不要向服务器发送请求。

一个随机添加到url的缓存破坏者可以工作,但也会填满浏览器缓存。在我的应用程序中,我想每隔几秒钟从网络摄像头下载一张5mb的图片。完全冻结你的电脑只需要一个小时或更少的时间。我仍然不知道为什么浏览器缓存不限制在一个合理的最大值,但这绝对是一个缺点。