在相同的URL上刷新图像为新的图片

451

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

我的问题是,如果我尝试在后台加载图像,然后更新页面上的图像,那么图像不会更改-但当我重新加载页面时,它会更新。

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

火狐浏览器看到的标头:

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

我需要强制刷新页面上的特定图片。有什么想法吗?


请参考以下链接:https://dev59.com/ZHVC5IYBdhLWcg3w-mZO - Philibert Perusse
19
十年过去了,我们仍然需要用hack的方法来实现这一点。浏览器制造商不能提供:img.reload()吗? - Daniel Wu
2
fetch是2022年更好的解决方案!请参见https://dev59.com/6XNA5IYBdhLWcg3wIqPr#66312176 - C-Y
23个回答

435

尝试在 URL 的结尾添加一个缓存破解器:

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

在创建图像时,这将自动附加当前时间戳,并使浏览器重新查找图像而不是检索缓存中的图像。


38
这不是一个很好的解决方案,因为它会淹没缓存(本地和上游)。Aya的答案有更好的处理方法。 - Tgr
1
此外,在其他地方重新显示相同的图像,没有后面的“缓存破坏器”,仍然会显示旧的缓存版本(至少在Firefox中),无论是?还是# :( - T4NK3R
3
你可以使用以下代码来缩减代码量:'image.jpg?' + (+new Date()) - Lev Lukomsky
7
这可以使用 Date.now() 来实现。 - vp_arth
2
为什么不使用 Math.random() - Gowtham
显示剩余5条评论

288

我看到很多关于如何实现这一点的不同答案,所以我想在这里总结一下它们(并添加我自己发明的第四种方法):


(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为您的图像提供服务,并向URL添加唯一的memcache破坏片段标识符,例如:

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

这里的思路是使用缓存控制头将图像放入浏览器缓存,但立即标记它们过时,以便每次重新显示时浏览器都必须与服务器检查它们是否已更改。这确保了浏览器的HTTP缓存始终返回图像的最新副本。然而,如果浏览器已经有一份内存中的图像副本,则通常会重复使用它,甚至不检查其HTTP缓存。为了防止这种情况发生,使用片段标识符:在内存中的图像src比较包括片段标识符,但在查询HTTP缓存之前被删除。(例如,image.jpg#Aimage.jpg#B可能都会从浏览器的HTTP缓存中的image.jpg条目中显示,但image.jpg#B永远不会使用自上次显示image.jpg#A时保留的内存中的图像数据显示)。 优点:合理利用HTTP缓存机制,并在图像未更改时使用缓存的图像。适用于无法处理静态图像URL中添加查询字符串的服务器 (因为服务器从不看到片段标识符 - 它们仅供浏览器使用)。 缺点:依赖浏览器对带有URL片段标识符的图像的行为有些可疑(或至少是文档不全) (但是,我已经在FF27、Chrome33和IE11中成功测试过了)。每次查看图像都仍会向服务器发送重新验证请求,这可能是过度的,如果图像很少更改和/或延迟是一个大问题(因为即使缓存的图像仍然有效,您也需要等待重新验证响应)。需要修改图像URL。 何时使用:当图像可能经常更改或需要由客户端间歇性地刷新而无需服务器端脚本参与,但仍需要缓存优势时使用。例如,轮询更新不规则每隔几分钟更新一次的现场网络摄像头图像。或者,如果您的服务器不允许静态图像URL上的查询字符串,则使用它替代(1)或(2)。 编辑2021:不再适用于最新的Chrome和Edge:这些浏览器中的内部内存缓存现在会忽略片段标识符(可能自从切换到Blink引擎以来?)。但请参见下面的方法(4),在这两个特定的浏览器上现在要简单得多,因此考虑将该方法与(4)的简化版本结合使用以覆盖这两个浏览器。

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

步骤如下:

  • 将要刷新的图片加载到一个隐藏的iframe中。 [编辑2021:对于Chrome和Edge,请加载带有<img>标签的HTML页面,而不是原始图像文件]。这只是一个设置步骤 - 如果需要,可以提前长时间完成实际刷新。此时,即使图像未能成功加载也没有关系!

  • [编辑2021:最近的Chrome和Edge现在无需执行此步骤]。搞定后,清空您页面或任何DOM节点中所有拷贝的该图像(即使是存储在JavaScript变量中的虚拟节点)。这是必要的,因为浏览器可能会从旧的内存拷贝中显示图像(尤其是IE11):在刷新HTTP缓存之前,您需要确保所有内存中的拷贝都被清除。如果其他JavaScript代码正在异步运行,还可能需要防止该代码同时创建要刷新的图像的新副本。

  • 调用iframe.contentWindow.location.reload(true)true强制绕过缓存,直接重新从服务器加载并覆盖现有的缓存副本。

  • [编辑2021:在最近的Chrome和Edge上不需要此步骤 - 在这些浏览器上,现有图像将在前面的步骤后自动更新!]完成重新加载后,恢复空白图片。它们应该现在显示来自服务器的新版本!

对于同域图像,可以直接将图像加载到iframe中。[编辑2021:不适用于Chrome、Edge]。对于跨域图像,您必须加载包含<img>标签中图像的HTML页面来自您的域,否则尝试调用iframe.contentWindow.reload(...)时会出现“访问被拒绝”错误。[在Chrome和Edge上也要这样做]。

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

缺点:依赖于JavaScript。不能保证在每个浏览器中完全正常工作(尽管我已成功在FF27、Chrome33和IE11中测试过)。与其他方法相比非常复杂。[编辑2021:除非您仅需要最近的Chrome和Edge支持,否则它非常简单。]

何时使用:当您拥有一组基本静态的图像需要缓存,但仍需要偶尔更新它们并获得立即的视觉反馈,以表明已进行更新时。特别是在刷新整个浏览器页面无法起作用的情况下,例如在某些基于AJAX构建的Web应用程序中。当方法(1)-(3)不可行时,因为由于某种原因您无法更改可能显示所需更新的图片的所有URL。(请注意,使用这三种方法会刷新图像,但如果另一个页面尝试显示该图像没有适当的查询字符串或片段标识符,则可能显示旧版本)。

以下提供了以相对健壮和灵活的方式实现此操作的详细信息:

假设您的网站包含位于URL路径/img/1x1blank.gif处的空白1x1像素.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 数组 [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进行一些更改。


我喜欢第四种方法的想法,但是你不能使用Iframe加载外部内容,对吗?我目前在单页Web应用程序中使用第三种方法,但我不喜欢即使模板的HTML重新加载,也必须重新加载整个页面才能获取新图像的事实。 - Emilios1995
@Emilios:此外,我不明白你关于必须重新加载整个页面的评论。方法(3)和(4)都可以在客户端JavaScript中实现,而无需重新加载除要刷新的一个图像之外的任何内容。对于方法(3),这意味着使用JavaScript将您的图像的“src”属性从(例如)image.jpg#123更改为image.jpg#124(或任何其他值,只要“#”后面的位数发生变化即可)。您能澄清一下您正在重新加载什么以及原因吗? - Doin
@Emilios:你确实可以将外部(跨域)内容加载到iframe中...但是你无法通过javascript访问它的contentWindow来执行reload(true)调用,这是该方法的关键部分...所以,不,方法(4)对于跨域内容不起作用。很好地发现了这一点;我会更新“缺点”以包括它。 - Doin
@Emilios:糟糕,我不会这样做:我意识到一个简单的修复(现在包含在我的答案中)可以使它适用于跨域图像(前提是您可以在服务器上放置一个服务器端脚本)。 - Doin
@pseudosavant:很不幸,我现在才注意到这个问题,但是很抱歉告诉你,你对我的代码所做的修改是有严重问题的。(公平地说,我认为我最初编写的回调代码也不正确)。我已经彻底重写了第四部分的说明和代码。你之前的代码从未清空图像(因此可能会以奇怪的方式失败,特别是在IE中,尤其是如果图像在多个位置显示),更糟糕的是,在开始完全重新加载后,它还立即删除了iframe,这可能意味着它只能间歇性地工作或根本无法工作。对不起! - Doin
显示剩余14条评论

216

作为一种替代方案...

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

...看起来好像...

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

只要返回正确的Cache-Control头,就足以欺骗浏览器缓存而不绕过任何上游缓存。尽管您可以使用...

Cache-Control: no-cache, must-revalidate

如果这样做,您将失去If-Modified-SinceIf-None-Match头部的好处,因此可以使用以下代码:

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

如果图片没有实际发生变化,这样做应该可以防止浏览器重新下载整个图片。在IE、Firefox和Chrome上进行了测试并且正常工作。令人烦恼的是,在Safari上除非使用......否则会失败。

Cache-Control: no-store

虽然这仍然可能比在上游缓存中填充数百个相同的图像更可取,特别是当它们运行在你自己的服务器上时。;-)

更新(2014-09-28):现在看起来Chrome也需要Cache-Control:no-store


1
太好了!花了很长时间来尝试加载我的网页图片,这些图片存在延迟加载问题。现在我刚刚通过使用您的解决方案(使用“#”,使用“?”对我不起作用)解决了它。非常感谢!!! - user304602
28
这里涉及到两个缓存:浏览器的常规HTTP缓存和最近显示的图像的内存缓存。后者通过完整的“src”属性进行索引,因此添加独特的片段标识符可确保图像不仅仅是从内存中提取。但是片段标识符不会作为HTTP请求的一部分发送,因此常规HTTP缓存将像平常一样被使用。这就是为什么这种技术能够起作用的原因。 - Doin
有几个头部缓存。实际上我不太懂英语,请问我应该使用哪一个?!我想要的是不仅缓存照片(比如验证码)而且还能缓存其他东西。所以 Cache-Control: max-age=0, must-revalidate 对我来说是好的吗? - Shafizadeh
1
它对我不起作用。在我的情况下唯一不同的是,我有一个指向控制器操作的URL,该操作从数据库中检索img。我为控制器操作添加了其他参数,因此我将其添加为“......&convert=true&t=”+ new Date().getTime();和“......&convert=true#”+ new Date().getTime();。我做错了什么吗? - shaffooo
1
为了避免对象创建和/或方法调用的开销,您可以使用递增整数作为缓存破坏器:newImage.src = "http://localhost/image.jpg#" + i++; - laindir

35

2021答案: 您可以简单地使用fetch,并将cache选项设置为'reload'以更新缓存:

fetch("my-image-url.jpg", {cache: 'reload', mode: 'no-cors'})

以下函数将更新缓存并且在页面中任何地方重新加载您的图像:

async function reloadImg(url) {
  await fetch(url, { cache: 'reload', mode: 'no-cors' })
  document.body.querySelectorAll(`img[src='${url}']`)
    .forEach(img => img.src = url)
}

它返回一个Promise,因此您可以像这样使用它:await reloadImg("my-image-url.jpg")(如果您愿意)。

现在,fetch API几乎无处不在可用(当然,除了IE)。


使用其他缓存值,例如“默认值”,可以很好地条件性地刷新具有适当的最后修改标头的陈旧资源。 - LHSnow
太好了!不需要担心清除缓存。 - Dale Ryan
不幸的是,在 Firefox 中,图像缓存是特殊的 - 它的“每个页面加载”缓存不会通过 fetch API 被破坏,也不会在禁用开发工具中的缓存时被破坏。截至版本 109。 - avl

10

创建新图像后,您是否将旧图像从DOM中删除并用新图像替换它?

您可能正在每次调用updateImage时获取新图像,但未将其添加到页面中。

有许多方法可以实现它。像这样的东西会起作用。

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

如果这样做还是有问题,那很可能是像其他答案所述的缓存问题。


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

然后在一些 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>

所以这个功能是在图片加载后,安排它在1秒钟后重新加载。我将其用于不同类型的家庭安防摄像头页面。


5
我有一个要求:1)不能向图像添加任何?var=xx 2)它应该可以跨域工作。
我真的很喜欢这个答案中的#4选项,但是有一个问题:
- 它在跨域方面存在可靠性问题(并且需要触及服务器代码)。
我快速而简单的解决方法是:
1. 创建隐藏的iframe。 2. 将当前页面加载到iframe中(是的,整个页面)。 3. iframe.contentWindow.location.reload(true); 4. 将图像源重新设置为本身。
这就是它。
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事件。


4

一种解决方法是像建议的那样,粗略地添加一些获取查询参数。

更好的解决方案是在您的HTTP标头中发出几个额外选项。

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

如果提供的日期是过去的日期,浏览器就不会缓存。HTTP/1.1中添加了Cache-Control,而must-revalidate标记表示代理服务器甚至在特殊情况下也不应该提供旧图像,而Pragma: no-cache对于当前现代浏览器和缓存来说并不是必需的,但可能有助于一些老旧实现的修复。


3
听起来这样做本应有效,但即使使用了黑客技巧,它仍然显示相同的图像。我会在问题中添加头信息。 - QueueHammer
我刚刚注意到你一遍又一遍地刷新同一个img标签。浏览器可能会检测到当你去设置src时,src没有改变,所以它不会费心去刷新。(由于这个检查是在DOM级别上进行的,与目标无关)。如果你在正在检索的图像的URL中添加“?”+数字,会发生什么? - Edward Kmett

4
这个回答基于上面几个回答,但稍作整合和简化,并将回答表述为一个JavaScript函数。
function refreshCachedImage(img_id) {
    var img = document.getElementById(img_id);
    img.src = img.src; // trick browser into reload
};

我需要解决一个问题,即动画SVG在第一次播放后无法重新启动。此技巧也适用于其他媒体,如音频和视频。

哇,易于理解,不会干扰头文件,太棒了!谢谢! - ZioBit
截至02/22,它似乎不再起作用了,至少在FF 96.0.3上从控制台中看到(叹气)。 - Giampaolo Ferradini
1
我还没有测试过,但使用img.src = img.src+'';应该可以强制重新加载。 - Mark Kortink

4
我最终做的是让服务器将该目录中任何图像的请求映射到我正在尝试更新的源。然后,我的计时器会在名称末尾添加一个数字,以便DOM将其视为新图像并加载它。
例如:
http://localhost/image.jpg
//and
http://localhost/image01.jpg

将请求相同的图像生成代码,但对于浏览器来说,它们看起来像不同的图像。

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
这个方法和使用查询字符串的解决方案一样会存在多次缓存图片的问题(Paolo和其他人提出了这个问题),并且需要进行服务器更改。 - TomG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接