如何强制网络浏览器不缓存图像

Posted

技术标签:

【中文标题】如何强制网络浏览器不缓存图像【英文标题】:How to force a web browser NOT to cache images 【发布时间】:2010-09-12 17:02:25 【问题描述】:

背景

我正在为两个公益网站编写和使用一个非常简单的基于 CGI (Perl) 的内容管理工具。它为网站管理员提供了用于填写字段(日期、地点、标题、描述、链接等)并保存的事件的 html 表单。在该表单上,我允许管理员上传与该事件相关的图像。在显示表单的 HTML 页面上,我还显示了上传图片的预览(HTML img 标签)。

问题

当管理员想要更改图片时,就会出现问题。他只需点击“浏览”按钮,选择一张新图片,然后按确定。这很好用。

图像上传后,我的后端 CGI 会正确处理上传并重新加载表单。

问题是显示的图像没有刷新。即使数据库拥有正确的图像,旧图像仍会显示。我已将其范围缩小到图像被缓存在网络浏览器中的事实。如果管理员在 Firefox/Explorer/Safari 中点击 RELOAD 按钮,一切都会正常刷新,新图像就会出现。

我的解决方案 - 不工作

我正在尝试通过编写一个 HTTP Expires 指令来控制缓存,该指令的日期很远。

Expires: Mon, 15 Sep 2003 1:00:00 GMT

请记住,我是管理人员,我并不关心页面是否需要更长的时间来加载,因为它们总是过期的。

但是,这也不起作用。

注意事项

上传图片时,其文件名不会保存在数据库中。它被重命名为 Image.jpg (使用它时简单的事情)。用新图像替换现有图像时,名称也不会改变。只是图像文件的内容发生了变化。

网络服务器由托管服务/ISP 提供。它使用 Apache。

问题

有没有办法强制网络浏览器不缓存此页面中的内容,甚至不缓存图像?

我正在处理使用数据库实际“保存文件名”的选项。这样,如果图片改变了,IMG标签的src也会改变。但是,这需要整个站点进行大量更改,如果我有更好的解决方案,我宁愿不这样做。此外,如果上传的新图片具有相同的名称,这仍然不起作用(比如图片经过了一些照片处理并重新上传)。

【问题讨论】:

我认为这个问题可以重做,以删除我写它时放在那里的所有“现在无用的上下文”。我认为标题是正确的,也是最佳答案。但是这个问题最初是为了给多种答案提供很大的空间而写的,没想到这么简单的解决方案。因此,对于前来寻求答案的人来说,这个问题有点令人费解。 【参考方案1】:

Armin Ronacher 的想法是正确的。问题是随机字符串可能会发生冲突。我会使用:

<img src="picture.jpg?1222259157.415" >

其中“1222259157.415”是服务器上的当前时间。 通过 javascript 使用 performance.now() 或通过 Python 使用 time.time() 生成时间

【讨论】:

一个重要的补充是你永远不能强制浏览器做任何事情。你所能做的就是提出友好的建议。实际遵循这些建议取决于浏览器和用户。浏览器可以随意忽略这一点,或者用户可以覆盖默认值。 乔尔,您最好在自己的答案中添加它。 只是一个想法,来这里参加聚会为时已晚-但是由于您可以控制图像更改,因此在图像更新时重命名图像可能会更好,而不是添加一个查询字符串。所以:例如 1222259157.jpg,而不是 picture.jpg?1222259157。这样它就会更新,但在重新访问时会重新缓存。 与其添加服务器时间,这将完全阻止缓存,为什么不在'?'之后添加文件的最后修改时间。这样,图像将被正常缓存,直到下一次更改。 Doin 的最后一条评论必须点赞!智能缓存确实很重要,为什么有人必须一遍又一遍地下载同一个文件?【参考方案2】:

简单修复:将随机查询字符串附加到图像:

<img src="foo.cgi?random=323527528432525.24234" >

HTTP RFC 所说的:

Cache-Control: no-cache

但这并不那么好用:)

【讨论】:

如何在普通html 标签中赋予Cache-Control: no-cache? 是否需要在响应头中出现?【参考方案3】:

我用php's file modified time function,例如:

echo <img  src='Images/image.png?" . filemtime('Images/image.png') . "'  />";

如果您更改图像,则使用新图像而不是缓存的图像,因为修改的时间戳不同。

【讨论】:

很棒的提示。如果修改时间更改(覆盖或更改),您将缓存和图像重新发送给客户端。 查看这里以获取另一个选项***.com/questions/56588850/… 绝对做到了。伟大的小费伙伴。 完美!只是一个额外的提示:"> @RicaGurgel 感谢以上内容,我尝试将其添加到 php 文件中,如下所示从服务器获取最新图像 " /> 它不起作用, 【参考方案4】:

我会使用:

<img src="picture.jpg?20130910043254">

其中“20130910043254”是文件的修改时间。

上传图片时,其文件名不会保存在数据库中。它被重命名为 Image.jpg(在使用它时简单地说明一下)。用新图像替换现有图像时,名称也不会改变。只是图像文件的内容发生了变化。

我认为有两种简单的解决方案:1)首先想到的解决方案(直截了当的解决方案,因为它们很容易想出),2)经过思考后最终得出的解决方案(因为它们易于使用)。显然,如果您选择重新考虑,您将不会总是受益。但我相信第二种选择被低估了。想想为什么php 如此受欢迎;)

【讨论】:

+1 我认为这比其他答案要好得多,因为使用 las 修改时间,当图像中没有任何变化时,您的服务器不会尝试多次将相同的图像提供给同一个浏览器.每次有请求时提取图像的最后写入时间有一点开销,但对于大图像,这比每次都必须返回图像要好,当图像发生变化时,用户将拥有新的图像。感谢您添加了这个漂亮的小东西。 嗯,可以将修改时间与图像的路径一起存储在数据库中。所以开销可能更小。另一方面,如果这些图像是我们正在讨论的源代码的一部分,那么也可能会缓存它们的修改时间。通过生成具有图像修改时间的脚本(例如images.php)。该脚本必须在每次提交时重新生成,并消除确定文件修改时间的开销。 @x-yori 这就是为什么我选择将此更新后的字段存储在数据库中而不是运行 filemtime ***.com/questions/56588850/…【参考方案5】:

使用 Class="NO-CACHE"

示例 html:

<div>
    <img class="NO-CACHE" src="images/img1.jpg" />
    <img class="NO-CACHE" src="images/imgLogo.jpg" />
</div>

jQuery:

    $(document).ready(function ()
               
        $('.NO-CACHE').attr('src',function ()  return $(this).attr('src') + "?a=" + Math.random() );
    );

javascript:

var nods = document.getElementsByClassName('NO-CACHE');
for (var i = 0; i < nods.length; i++)

    nods[i].attributes['src'].value += "?a=" + Math.random();

结果: src="images/img1.jpg" => src="images/img1.jpg?a=0.08749723793963926"

【讨论】:

简单,优雅,不需要服务器端渲染。不错!【参考方案6】:

您可以编写一个代理脚本来提供图像 - 不过这需要更多的工作。像这样的东西:

HTML:

<img src="image.php?img=imageFile.jpg&some-random-number-262376" />

脚本:

// PHP
if( isset( $_GET['img'] ) && is_file( IMG_PATH . $_GET['img'] ) ) 

  // read contents
  $f = open( IMG_PATH . $_GET['img'] );
  $img = $f.read();
  $f.close();

  // no-cache headers - complete set
  // these copied from [php.net/header][1], tested myself - works
  header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Some time in the past
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
  header("Cache-Control: no-store, no-cache, must-revalidate"); 
  header("Cache-Control: post-check=0, pre-check=0", false); 
  header("Pragma: no-cache"); 

  // image related headers
  header('Accept-Ranges: bytes');
  header('Content-Length: '.strlen( $img )); // How many bytes we're going to send
  header('Content-Type: image/jpeg'); // or image/png etc

  // actual image
  echo $img;
  exit();

实际上,图像 src 中的无缓存标头或随机数应该就足够了,但是因为我们想要防弹..

【讨论】:

Yours 是一个很好的解决方案,除了 Pragma 不是响应标头。 @Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 似乎另有说法。【参考方案7】:

我检查了网上所有的答案,最好的一个似乎是:(实际上不是)

<img src="image.png?cache=none">

一开始。

但是,如果添加 cache=none 参数(即静态“none”字样),则不会产生任何影响,浏览器仍会从缓存中加载。

这个问题的解决方法是:

<img src="image.png?nocache=<?php echo time(); ?>">

您基本上添加 unix 时间戳以使参数动态且没有缓存,它起作用了。

但是,我的问题有点不同: 我正在加载动态生成的 php 图表图像,并使用 $_GET 参数控制页面。我希望在 URL GET 参数保持不变时从缓存中读取图像,并且在 GET 参数更改时不缓存。

为了解决这个问题,我需要对 $_GET 进行哈希处理,但由于它是数组,所以这里是解决方案:

$chart_hash = md5(implode('-', $_GET));
echo "<img src='/images/mychart.png?hash=$chart_hash'>";

编辑

虽然上述解决方案工作得很好,但有时您希望在文件更改之前提供缓存版本。 (使用上述解决方案,它会完全禁用该图像的缓存) 因此,要从浏览器提供缓存图像,直到图像文件使用发生变化:

echo "<img src='/images/mychart.png?hash=" . filemtime('mychart.png') . "'>";

filemtime() 获取文件修改时间。

【讨论】:

filemtime 解决方案更好,因为md5 需要大量处理能力。 花了几天时间试图让基于 Chromium 的应用程序停止缓存图像。 ?nocache with time echo 解决了这个问题。谢谢!【参考方案8】:

我是一名新的编码员,但这是我想出的,以阻止浏览器缓存并保留我的网络摄像头视图:

<meta Http-Equiv="Cache" content="no-cache">
<meta Http-Equiv="Pragma-Control" content="no-cache">
<meta Http-Equiv="Cache-directive" Content="no-cache">
<meta Http-Equiv="Pragma-directive" Content="no-cache">
<meta Http-Equiv="Cache-Control" Content="no-cache">
<meta Http-Equiv="Pragma" Content="no-cache">
<meta Http-Equiv="Expires" Content="0">
<meta Http-Equiv="Pragma-directive: no-cache">
<meta Http-Equiv="Cache-directive: no-cache">

不确定什么适用于什么浏览器,但它确实适用于某些浏览器: IE:刷新网页和重新访问网站(不刷新)时工作。 CHROME:仅在刷新网页时有效(即使在重新访问之后)。 SAFARI 和 iPad:不行,我必须清除历史记录和网络数据。

对 SAFARI/iPad 有什么想法吗?

【讨论】:

【参考方案9】:

上传图片时,其文件名不会保存在数据库中。它被重命名为 Image.jpg (使用它时简单的事情)。

改变这个,你就解决了你的问题。我使用时间戳,就像上面提出的解决方案一样:Image-.jpg

大概,通过保持图像的相同文件名来避免的任何问题都可以克服,但你没有说它们是什么。

【讨论】:

【参考方案10】:

您的问题是,尽管有 Expires: 标头,但您的浏览器仍在重新使用更新前图像的内存副本,而不是检查其缓存。

我在管理后端为类似商店的网站上传产品图片时遇到了非常相似的情况,就我而言,我认为最好的选择是使用 javascript 来强制刷新图片,而不使用任何 URL 修改其他人已经在这里提到的技术。相反,我将图像 URL 放入隐藏的 IFRAME 中,在 IFRAME 的窗口上称为 location.reload(true),然后在页面上替换我的图像。这会强制刷新图像,不仅在我所在的页面上,而且在我访问的任何后续页面上 - 客户端或服务器都不必记住任何 URL 查询字符串或片段标识符参数。

我在回答 here 中发布了一些代码来执行此操作。

【讨论】:

【参考方案11】:

在我看来,禁用图像缓存是个坏主意。完全没有。

这里的根本问题是 - 如何在服务器端更新图像时强制浏览器更新图像。

同样,从我个人的角度来看,最好的解决方案是禁用直接访问图像。而是通过服务器端过滤器/servlet/其他类似工具/服务访问图像。

在我的情况下,它是一个休息服务,它返回图像并附加 ETag 作为响应。该服务保留所有文件的哈希,如果文件更改,则更新哈希。它在所有现代浏览器中都能完美运行。是的,实现它需要时间,但这是值得的。

唯一的例外是网站图标。由于某些原因,它不起作用。我无法强制浏览器从服务器端更新其缓存。 ETags、Cache Control、Expires、Pragma headers,没有任何帮助。

在这种情况下,在 url 中添加一些随机/版本参数似乎是唯一的解决方案。

【讨论】:

您可能想要禁用图像缓存的一个原因是允许引用定期更新的动态生成的图像。 @Manngo,你仔细阅读我的评论了吗?【参考方案12】:

添加时间戳&lt;img src="picture.jpg?t=&lt;?php echo time();?&gt;"&gt;

总是会在最后给你的文件一个随机数并停止缓存

【讨论】:

值得注意的是,实施此解决方案会给每个请求的服务器带来压力,并且不一定会起作用,因为浏览器可以缓存包含初始请求时生成的时间的 php 页面,该页面被缓存。这只有在页面也有动态查询字符串时才能可靠地工作。【参考方案13】:

由于您和客户端之间可能存在行为不良的透明代理,因此完全保证图像不会被缓存的唯一方法是为它们提供唯一的 uri,例如将时间戳标记为查询字符串或路径的一部分。

如果该时间戳对应于图像的最后更新时间,那么您可以在需要时缓存并在正确的时间提供新图像。

【讨论】:

【参考方案14】:

我假设原始问题与存储有一些文本信息的图像有关。因此,如果您在生成 src=... url 时可以访问文本上下文,请考虑存储/使用图像字节的 CRC32,而不是无意义的随机或时间戳。然后,如果显示有大量图像的页面,则只会重新加载更新的图像。最终,如果 CRC 存储是不可能的,它可以在运行时计算并附加到 url。

【讨论】:

或者,使用图像的最后修改时间,而不是 CRC,更好!【参考方案15】:

理想情况下,您应该为每个网页添加一个按钮/键绑定/菜单,并带有同步内容的选项。

为此,您将跟踪可能需要同步的资源,并使用 xhr 使用动态查询字符串探测图像,或者在运行时使用 src 使用动态查询字符串创建图像。然后使用广播机制通知所有 正在使用资源更新的网页组件以使用附加到其 url 的动态查询字符串的资源。

一个简单的例子如下所示:

通常,图像会显示并缓存,但如果用户按下按钮,则会向资源发送一个 xhr 请求,并附加一个时间查询字符串;由于可以假设每次按下的时间都不同,因此它将确保浏览器将绕过缓存,因为它无法判断资源是基于查询在服务器端动态生成的,还是静态的忽略查询的资源。

结果是,您可以避免让所有用户一直用资源请求轰炸您,但同时,允许用户在怀疑资源不同步时更新其资源的机制。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="mobile-web-app-capable" content="yes" />        
    <title>Resource Synchronization Test</title>
    <script>
function sync() 
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function() 
        if (this.readyState == 4 && this.status == 200)             
            var images = document.getElementsByClassName("depends-on-resource");

            for (var i = 0; i < images.length; ++i) 
                var image = images[i];
                if (image.getAttribute('data-resource-name') == 'resource.bmp') 
                    image.src = 'resource.bmp?i=' + new Date().getTime();                
                
            
        
    
    xhr.open('GET', 'resource.bmp', true);
    xhr.send();

    </script>
  </head>
  <body>
    <img class="depends-on-resource" data-resource-name="resource.bmp" src="resource.bmp"></img>
    <button onclick="sync()">sync</button>
  </body>
</html>

【讨论】:

【参考方案16】:

您必须使用唯一的文件名。像这样

<img src="cars.png?1287361287" >

但是这种技术意味着高服务器使用率和带宽浪费。 相反,您应该使用版本号或日期。示例:

<img src="cars.png?2020-02-18" >

但您希望它永远不会从缓存中提供图像。对于这一点,如果页面不使用页面缓存,则可以使用 PHP 或服务器端。

<img src="cars.png?<?php echo time();?>" >

但是,它仍然没有效果。原因:浏览器缓存... 最后但最有效的方法是 Native JAVASCRIPT。这段简单的代码查找所有图像具有“NO-CACHE”类并使图像几乎是唯一的。把它放在脚本标签之间。

var items = document.querySelectorAll("img.NO-CACHE");
for (var i = items.length; i--;) 
    var img = items[i];
    img.src = img.src + '?' + Date.now();

用法

<img class="NO-CACHE" src="https://upload.wikimedia.org/wikipedia/commons/6/6a/JavaScript-logo.png" >

这样的结果

https://example.com/image.png?1582018163634

【讨论】:

【参考方案17】:

所有答案都是有效的,因为它工作正常。但是这样一来,浏览器也会在每次使用不同的 URL 加载该图像时在缓存中创建另一个文件。因此,不要通过向 URL 添加一些查询参数来更改 URL。我认为我们应该更新浏览器缓存。

caches.open('YOUR_CACHE_NAME').then(cache => 
  const url = 'URL_OF_IMAGE_TO_UPDATE'
  fetch(url).then(res => 
    cache.put(url, res.clone())
  )
)

cache.put 使用新响应更新缓存。

了解更多:https://developer.mozilla.org/en-US/docs/Web/API/Cache/put

【讨论】:

【参考方案18】:

我制作了一个 PHP 脚本,它会自动在所有图像和链接上附加时间戳。你只需要在你的页面中包含这个脚本。享受吧!

http://alv90.altervista.org/how-to-force-the-browser-not-to-cache-images/

【讨论】:

【参考方案19】:

最好的解决方案是在源 href 的末尾提供当前时间,例如 &lt;img src="www.abc.com/123.png?t=current_time"&gt;

这将消除引用已缓存图像的机会。 要获取最近的时间,可以在 jQuery 或 javascript 中使用 performance.now() 函数。

【讨论】:

发布这个答案有什么意义?它与 13 年前公认的答案相同(甚至取决于您推荐用于获取时间的功能)。

以上是关于如何强制网络浏览器不缓存图像的主要内容,如果未能解决你的问题,请参考以下文章

如何强制浏览器重新缓存图像[重复]

如何强制浏览器使用 php 刷新缓存?

前端缓存的理解 或者 前端数据持久化的理解(强制缓存、协商缓存)

理解http浏览器的协商缓存和强制缓存

获取 url 时如何强制网络浏览器使用 POST?

如何在网络浏览器上阻止图像