【问题标题】:Refresh image with a new one at the same url用同一网址上的新图片刷新图片
【发布时间】:2015-01-17 08:47:57
【问题描述】:

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

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

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

我需要强制刷新页面上的那个图像。有什么想法吗?

【问题讨论】:

标签: javascript image url refresh


【解决方案1】:

尝试在 url 的末尾添加一个 cachebreaker:

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

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

【讨论】:

  • 不是一个很好的解决方案,因为它会淹没缓存(本地和上游)。 Aya's answer 有更好的处理方法。
  • 另外,在其他地方重新显示相同的图像,以后没有“缓存破坏器”,仍然显示旧的缓存版本(至少在 Firefox 中)?和 # :(
  • 你可以用这个减少代码:'image.jpg?' + (+new Date())
  • 这里有Date.now()
  • 为什么不Math.random()
【解决方案2】:

尝试使用毫无价值的查询字符串使其成为唯一的 url:

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

    setTimeout(updateImage, 1000);
}

【讨论】:

  • 将 WQS 添加到代码中,并验证请求已被接受,并且浏览器将响应视为来自地址 + WQS 而没有刷新图像。
【解决方案3】:

一个答案是像建议的那样随意添加一些获取查询参数。

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

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

通过提供过去的日期,浏览器不会缓存它。 Cache-Control 是在 HTTP/1.1 中添加的,must-revalidate 标签表明即使在情有可原的情况下,代理也不应该提供旧图像,Pragma: no-cache 对于当前的现代浏览器/缓存并不是真正必要的,但可能有助于一些糟糕的破旧实现。

【讨论】:

  • 这听起来像是可行的,但它仍然显示相同的图像,即使有黑客攻击。我会将标题信息添加到问题中。
  • 我刚刚注意到您一遍又一遍地刷新同一个 img 标签。当您设置 src 时,浏览器可能会检测到 src 没有更改,因此它不会费心刷新。 (由于此检查发生在 DOM 级别,与目标无关)。如果添加“?”会发生什么? + number -- 要检索的图像的 url?
【解决方案4】:

创建新图像后,您是否从 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);
}

在开始工作后,如果仍然存在问题,则可能是缓存问题,就像其他答案所说的那样。

【讨论】:

    【解决方案5】:

    我最终做的是让服务器将对该目录中的图像的任何请求映射到我尝试更新的源。然后我让计时器在名称的末尾附加一个数字,以便 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);
    }
    

    【讨论】:

    • 这将与查询字符串解决方案(Paolo 和其他一些)存在相同的缓存图像的多个副本的问题,并且需要更改服务器。
    【解决方案6】:

    作为...的替代品

    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

    【讨论】:

    • 太棒了!经过大量时间尝试加载延迟加载的网络图像后,我刚刚通过应用您的解决方案解决了它(使用“#”,使用“?”对我不起作用)。非常感谢!!!
    • 这里涉及到 两个 缓存:浏览器​​的常规 HTTP 缓存和最近显示的图像的内存缓存。后一种内存缓存由完整的src 属性索引,因此添加唯一的片段标识符可确保图像不是简单地从内存中提取的。但是片段标识符不会作为 HTTP 请求的一部分发送,因此常规 HTTP 缓存将照常使用。这就是这种技术有效的原因。
    • 有几个头部缓存。其实我不太懂英文,你能告诉我应该用哪一个吗?!我想要一些不只缓存更改的照片(如验证码)并缓存其他内容的东西。那么Cache-Control: max-age=0, must-revalidate 对我有好处吗?
    • 它对我不起作用。在我的情况下,唯一不同的是我有一个控制器操作的 url,它从 db 中检索 img。我对控制器操作有其他参数,所以我将其添加为 "......&convert=true&t=" + new Date().getTime();和 "......&convert=true#" + new Date().getTime();。有什么我做错了吗?
    • 为了避免对象创建和/​​或方法调用的开销,您可以使用递增整数作为缓存破坏器:newImage.src = "http://localhost/image.jpg#" + i++;
    【解决方案7】:

    我通过 servlet 发回数据解决了这个问题。

    response.setContentType("image/png");
    response.setHeader("Pragma", "no-cache");
    response.setHeader("Cache-Control", "no-cache, must-revalidate");
    response.setDateHeader("Expires", 0);
    
    BufferedImage img = ImageIO.read(new File(imageFileName));
    
    ImageIO.write(img, "png", response.getOutputStream());
    

    然后从页面中给它一个带有一些参数的 servlet 来获取正确的图像文件。

    <img src="YourServlet?imageFileName=imageNum1">
    

    【讨论】:

      【解决方案8】:

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


      (1)在URL中添加唯一的cache-busting查询参数,如:

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

      优点: 100% 可靠、快速且易于理解和实施。

      缺点:完全绕过缓存,这意味着只要图像在视图之间发生变化,就会出现不必要的延迟和带宽使用。可能会用许多完全相同的图像的副本填充浏览器缓存(和任何中间缓存)!另外,需要修改图片网址。

      何时使用:在图像不断变化时使用,例如实时网络摄像头源。如果您使用此方法,请确保使用Cache-control: no-cache HTTP 标头提供图像本身!!!(通常可以使用 .htaccess 文件进行设置)。否则,您将逐渐用旧版本的图像填充缓存!


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

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

      (这是 PHP 服务器端代码,但这里重要的一点是 ?m=[file last-modified time] 查询字符串附加到文件名。

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

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

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


      (3) 使用标题Cache-control: max-age=0, must-revalidate 提供图片,并在 URL 中添加唯一的 memcache-busting 片段标识符,例如:

      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强制刷新特定图像,首先将其加载到隐藏的&lt;iframe&gt;,然后在iframe的contentWindow上调用location.reload(true)

      步骤如下:

      • 将要刷新的图像加载到隐藏的 iframe 中。 [EDIT 2021:对于 Chrome 和 Edge,加载带有 &lt;img&gt; 标签的 HTML 页面,而不是原始图像文件]。 这只是一个设置步骤 - 它可以如果需要,可以提前很长时间进行实际刷新。即使在这个阶段无法加载图像也没关系!

      • [EDIT 2021:在最近的 Chrome 和 Edge 中现在不需要此步骤]。 完成后,将页面上该图像的所有副本清空或任何 DOM 节点中的任何位置(甚至是存储在 javascript 变量中的页外节点)。这是必要的,因为浏览器可能会显示来自陈旧内存副本的图像(IE11 尤其如此):您需要确保在刷新 HTTP 缓存之前清除所有 内存 副本。如果其他 javascript 代码正在异步运行,您可能还需要同时阻止该代码创建待刷新图像的新副本。

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

      • [EDIT 2021:在最近的 Chrome 和 Edge 中,现在不需要此步骤 - 在这些浏览器上,现有图像只会在上一步之后自动更新!] 一次它已完成重新-加载,恢复空白图像。他们现在应该显示来自服务器的新版本!

      对于同域图片,您可以直接将图片加载到 iframe 中。 [EDIT 2021: Not on Chrome, Edge]. 对于跨域图像,您必须从您的域中加载一个 HTML 页面包含&lt;img&gt; 标记中的图像,否则在尝试调用iframe.contentWindow.reload(...) 时会收到“拒绝访问”错误。 [对 Chrome 和 Edge 也这样做]。

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

      缺点:依赖于 Javascript。不能 100% 保证在每个浏览器中都能正常工作(尽管我已经在 FF27、Chrome33 和 IE11 中成功测试过)。相对于其他方法非常复杂。 [EDIT 2021:除非您只需要最近的 Chrome 和 Edge 支持,否则它会简单得多]。

      何时使用:当您有一组基本静态的图像想要缓存,但您仍然需要能够偶尔更新它们并获得更新所花费的即时视觉反馈地方。 (尤其是当仅刷新整个浏览器页面不起作用时,例如在某些基于 AJAX 构建的 Web 应用程序中)。当方法 (1)-(3) 不可行时,因为(无论出于何种原因)您无法更改所有可能显示您需要更新的图像的 URL。 (请注意,使用这 3 种方法会刷新图像,但如果 another 页面随后尝试显示该图像 没有 适当的查询字符串或片段标识符,它可能会显示较旧的版本)。

      下面给出了以一种健壮和灵活的方式实现它的细节:

      假设您的网站在 URL 路径 /img/1x1blank.gif 处包含一个空白的 1x1 像素 .gif,并且还具有以下一行 PHP 脚本(仅在对 跨域 应用强制刷新时需要图像,当然可以用任何服务器端脚本语言重写)在 URL 路径/echoimg.php

      <img src="<?=htmlspecialchars(@$_GET['src'],ENT_COMPAT|ENT_HTML5,'UTF-8')?>">
      

      然后,这里是您如何在 Javascript 中完成所有这些的实际实现。它看起来有点复杂,但是有很多 cmets,重要的函数只是 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 进行一些更改。

      【讨论】:

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

      function reloadImage(imageId)
      {
         path = '../showImage.php?cache='; //for example
         imageObject = document.getElementById(imageId);
         imageObject.src = path + (new Date()).getTime();
      }
      <img src='../showImage.php' id='myimage' />
      
      <br/>
      
      <input type='button' onclick="reloadImage('myimage')" />

      【讨论】:

      • 请向 OP 解释如何以及为什么这会有所帮助,而不仅仅是粘贴代码
      • 我不认为../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit) 是一个有效的文件名...至少这是它试图加载的内容...
      • path='../showImage.php';更改为path='../showImage.php?';
      【解决方案10】:

      这是我的解决方案。这很简单。帧调度可能会更好。

      <!doctype html>
      <html>
          <head>
              <meta charset="utf-8">      
              <title>Image Refresh</title>
          </head>
      
          <body>
      
          <!-- Get the initial image. -->
          <img id="frame" src="frame.jpg">
      
          <script>        
              // Use an off-screen image to load the next frame.
              var img = new Image();
      
              // When it is loaded...
              img.addEventListener("load", function() {
      
                  // Set the on-screen image to the same source. This should be instant because
                  // it is already loaded.
                  document.getElementById("frame").src = img.src;
      
                  // Schedule loading the next frame.
                  setTimeout(function() {
                      img.src = "frame.jpg?" + (new Date).getTime();
                  }, 1000/15); // 15 FPS (more or less)
              })
      
              // Start the loading process.
              img.src = "frame.jpg?" + (new Date).getTime();
          </script>
          </body>
      </html>
      

      【讨论】:

        【解决方案11】:

        在很大程度上基于 Doin 的 #4 代码,下面的示例使用 document.write 而不是 iframe 中的 src 来简化代码以支持 CORS。也只专注于破坏浏览器缓存,而不是重新加载页面上的每个图像。

        下面是用typescript 编写的,并使用angular $q 承诺库,仅供参考,但应该很容易移植到香草javascript。方法意味着存在于打字稿类中。

        返回一个将在 iframe 完成重新加载时解决的承诺。没有经过大量测试,但对我们来说效果很好。

            mmForceImgReload(src: string): ng.IPromise<void> {
                var deferred = $q.defer<void>();
                var iframe = window.document.createElement("iframe");
        
                var firstLoad = true;
                var loadCallback = (e) => {
                    if (firstLoad) {
                        firstLoad = false;
                        iframe.contentWindow.location.reload(true);
                    } else {
                        if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
                        deferred.resolve();
                    }
                }
                iframe.style.display = "none";
                window.parent.document.body.appendChild(iframe);
                iframe.addEventListener("load", loadCallback, false);
                iframe.addEventListener("error", loadCallback, false);
                var doc = iframe.contentWindow.document;
                doc.open();
                doc.write('<html><head><title></title></head><body><img src="' + src + '"></body></html>');
                doc.close();
                return deferred.promise;
            }
        

        【讨论】:

        • 为了防止 XSS 错误,您应该使用 + encodeURI(src) +iframe 中正确转义 src
        【解决方案12】:
        document.getElementById("img-id").src = document.getElementById("img-id").src
        

        将自己的 src 设置为它的 src。

        【讨论】:

          【解决方案13】:

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

          我真的很喜欢this answer 中的#4 选项,但是:

          • 它在可靠地使用跨域时存在问题(并且需要接触服务器代码)。

          我快速而肮脏的方式是:

          1. 创建隐藏的 iframe
          2. 将当前页面加载到其中(是的,整个页面)
          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-events。

          【讨论】:

            【解决方案14】:

            我使用了以下概念,首先将图像与错误(缓冲区)url 绑定,然后将其与有效 url 绑定。

            imgcover.ImageUrl = ConfigurationManager.AppSettings["profileLargeImgPath"] + "Myapp_CoverPic_" + userid + "Buffer.jpg";
            
            imgcover.ImageUrl = ConfigurationManager.AppSettings["profileLargeImgPath"] + "Myapp_CoverPic_" + userid + ".jpg";
            

            这样,我强制浏览器使用有效的 url 刷新。

            【讨论】:

              【解决方案15】:
              <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 秒内重新加载。我在带有不同类型家庭安全摄像头的页面上使用它。

              【讨论】:

                【解决方案16】:

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

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

                【讨论】:

                • 投反对票。因为它只是@Mahmoud 代码的稍微修改的副本,但相比之下,这里不是刷新图像
                【解决方案17】:

                不需要new Date().getTime() 恶作剧。您可以通过使用不可见的虚拟图像并使用 jQuery .load() 来欺骗浏览器,然后每次都创建一个新图像:

                <img src="" id="dummy", style="display:none;" />  <!-- dummy img -->
                <div id="pic"></div>
                
                <script type="text/javascript">
                  var url = whatever;
                  // You can repeat the following as often as you like with the same url
                  $("#dummy").load(url);
                  var image = new Image();
                  image.src = url;
                  $("#pic").html("").append(image);
                </script>
                

                【讨论】:

                  【解决方案18】:

                  简单的解决方案:将此标头添加到响应中:

                  Cache-control: no-store
                  

                  这个权威页面清楚地解释了这个工作原理:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

                  这也解释了为什么no-cache 不起作用。

                  其他答案无效,因为:

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

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

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

                  【讨论】:

                    【解决方案19】:

                    我改进了 AlexMA 的脚本,用于在网页上显示我的网络摄像头,该网页会定期上传具有相同名称的新图像。我遇到的问题是,有时由于图像损坏或未完整(上传)加载的图像而导致图像闪烁。为了防止闪烁,我检查了图像的自然高度,因为我的网络摄像头图像的大小没有改变。只有加载的图像高度符合原始图像高度时,才会在页面上显示完整的图像。

                      <h3>Webcam</h3>
                      <p align="center">
                        <img id="webcam" title="Webcam" onload="updateImage();" src="https://www.your-domain.com/webcam/current.jpg" alt="webcam image" width="900" border="0" />
                    
                        <script type="text/javascript" language="JavaScript">
                    
                        // off-screen image to preload next image
                        var newImage = new Image();
                        newImage.src = "https://www.your-domain.com/webcam/current.jpg";
                    
                        // remember the image height to prevent showing broken images
                        var height = newImage.naturalHeight;
                    
                        function updateImage()
                        {
                            // for sure if the first image was a broken image
                            if(newImage.naturalHeight > height)
                            {
                              height = newImage.naturalHeight;
                            }
                    
                            // off-screen image loaded and the image was not broken
                            if(newImage.complete && newImage.naturalHeight == height) 
                            {
                              // show the preloaded image on page
                              document.getElementById("webcam").src = newImage.src;
                            }
                    
                            // preload next image with cachebreaker
                            newImage.src = "https://www.your-domain.com/webcam/current.jpg?time=" + new Date().getTime();
                    
                            // refresh image (set the refresh interval to half of webcam refresh, 
                            // in my case the webcam refreshes every 5 seconds)
                            setTimeout(updateImage, 2500);
                        }
                    
                        </script>
                    </p>
                    

                    【讨论】:

                      【解决方案20】:

                      将图像的第二个副本放在同一位置,然后删除原始图像。

                      function refreshImg(ele){
                          ele.insertAdjacentHTML('beforebegin',ele.outerHTML);
                          ele.parentNode.removeChild(ele);
                      }
                      

                      这将有效地刷新图像。

                      跨浏览器insertAdjacentHTMLouterHTMLparentNoderemoveChild 都是 crossbrowser

                      在性能方面,在大多数情况下,性能损失很可能可以忽略不计。 @Paolo Bergantino's 答案可能比这个功能更好。使用他的答案只会影响一个 DOM 元素。具有此功能的两个元素。

                      【讨论】:

                        【解决方案21】:

                        我在使用 Unsplash 随机图像功能时遇到了同样的问题。在 URL 末尾添加一个虚拟查询字符串的想法是正确的,但在这种情况下,完全随机的参数不起作用(我试过了)。我可以想象它对于其他一些服务也是一样的,但是对于 unsplash,参数需要是sig,所以你的图像 URL 将是,例如,http://example.net/image.jpg?sig=RANDOM 其中随机是一个随机字符串,当你更新它。我用了Math.random()*100,但日期也很合适。

                        您需要执行上述操作,因为没有它,浏览器会看到该路径处的图像已经加载,并将使用该缓存的图像来加快加载速度。

                        https://github.com/unsplash/unsplash-source-js/issues/9

                        【讨论】:

                          【解决方案22】:

                          您可以简单地使用fetch 并将cache option 设置为'reload' 来更新缓存:

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

                          以下函数将更新缓存在您的页面中各处重新加载您的图片:

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

                          它返回一个承诺,因此您可以根据需要像await reloadImg("my-image-url.jpg") 一样使用它。

                          现在 fetch API 是 available almost everywhere(当然,IE 除外)。

                          【讨论】:

                            【解决方案23】:

                            此答案基于上述几个答案,但将它们统一和简化了一点,并将答案转换为 JavaScript 函数。

                            function refreshCachedImage(img_id) {
                                var img = document.getElementById(img_id);
                                img.src = img.src; // trick browser into reload
                            };
                            

                            我需要解决动画 SVG 在第一次播放后无法重新启动的问题。

                            此技巧也适用于音频和视频等其他媒体。

                            【讨论】:

                            • 哇,通俗易懂,标题不乱,太棒了!谢谢!
                            猜你喜欢
                            • 2015-09-17
                            • 2020-11-27
                            • 2015-08-07
                            • 2019-02-16
                            • 2021-09-14
                            • 1970-01-01
                            • 2015-03-04
                            • 1970-01-01
                            相关资源
                            最近更新 更多