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

背景资料

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

问题

当管理员希望更改图片时会发生问题。他只需要点击“浏览”按钮,选择一张新图片,然后按 OK 键。这个很好用。

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

问题是显示的 没有图像会被刷新。即使数据库保存了正确的图像,仍然会显示旧图像。我已经把范围缩小到图像缓存在浏览器中这一事实。如果管理员点击 Firefox/Explorer/Safari 中的 RELOAD 按钮,所有东西都会被刷新,新图像就会出现。

我的解决方案——不起作用

我试图控制缓存通过写一个 HTTP 过期指令与日期非常遥远的过去。

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

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

但是,这也不起作用。

笔记

上传图像时,图像的文件名不保存在数据库中。它被重命名为 图片 JPG(在使用它时简单地将东西取出)。当用新图像替换现有图像时,名称也不会改变。只是图像文件的内容发生了变化。

Webserver 由托管服务/ISP 提供,它使用 Apache。

提问

有没有办法强制浏览器不要缓存这个页面上的内容,甚至是图片?

我正在同数据库中的“保存文件名”选项进行权衡。这样,如果图像发生了变化,IMG 标记的 src 也会发生变化。然而,这需要在整个网站很多变化,我宁愿不这样做,如果我有一个更好的解决方案。此外,如果上传的新图片具有相同的名称,这仍然不会工作(比如说图片被 PS 了一点,然后重新上传)。

252560 次浏览

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

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

HTTP RFC 说:

Cache-Control: no-cache

但是这个方法并不管用:)

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

如果时间戳对应于图像的最后一次更新时间,那么可以在需要时进行缓存,并在正确的时间为新图像提供服务。

阿明 · 罗纳赫的想法是正确的。问题是随机的字符串会碰撞。我会用:

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

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

你可以编写一个代理脚本来提供图片——不过这需要更多的工作:

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 上的随机数应该足够了,但是因为我们想要做到万无一失。.

上传图像时,图像的文件名不保存在数据库中。它被重命名为 Image.jpg (在使用它的时候简单地把东西取出来)。

改变这一点,您就解决了问题。我使用时间戳,就像上面提出的解决方案一样: 图像-< 时间戳 > . jpg

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

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

例如,我使用 PHP 文件修改时间函数:

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

如果您更改了图像,那么将使用新图像而不是缓存的图像,这是由于具有不同的修改时间戳。

我会用:

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

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

上传图像时,图像的文件名不保存在数据库中。它被重命名为 Image.jpg (在使用它的时候简单地把东西取出来)。当用新图像替换现有图像时,名称也不会改变。只是图像文件的内容发生了变化。

我认为有两种简单的解决方案: 1)那些首先想到的(直接的解决方案,因为它们很容易想出来) ,2)那些经过深思熟虑后最终得到的(因为它们很容易使用)。显然,如果你仔细想想,你不会总是受益。但我认为,第二种选择被低估了。想想为什么 php这么受欢迎;)

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

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

我张贴了一些代码来做这个在我的答案 给你

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

<img src="image.png?cache=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 ()获取文件修改时间。

在我看来,禁用图像缓存是一个糟糕的主意。

这里的根本问题是-如何迫使浏览器更新图像,当它已经在服务器端更新。

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

在我的例子中,它是一个 rest 服务,返回 image 并附加 ETag 作为响应。该服务保持所有文件的哈希,如果文件被更改,哈希将被更新。它在所有现代浏览器中都能完美运行。是的,实现它需要时间,但它是值得的。

唯一的例外是图标。由于某些原因,它不起作用。我不能强迫浏览器从服务器端更新它的缓存。ETags,缓存控制,过期,Pragma 头,没有任何帮助。

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

我是一个新的程序员,但这是我想出来的,以阻止浏览器缓存和保持我的网络摄像头视图:

<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 有什么想法吗?

使用 类 = “ 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? a = 0.08749723793963926”

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

将始终给你的文件一个随机数在结束,并停止缓存

理想情况下,您应该在每个网页上添加一个按钮/密钥绑定/菜单,并提供一个同步内容的选项。

为此,您需要跟踪可能需要同步的资源,并使用 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>

我编写了一个 PHP 脚本,它可以自动在所有图像和链接上追加时间戳。您只需要在页面中包含这个脚本。好好享受吧!

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

您必须使用唯一的文件名,如下所示

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

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

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

但是您希望它永远不要从缓存服务映像。对于 如果页不使用页缓存,则为,可以使用 PHP 或服务器端。

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

原因: 浏览器缓存..。 最后一个但是最有效的方法是 NativeJAVASCRIPT。这个简单的代码 找到所有的图像带有一个“ 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" alt="">

像这样的结果

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

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

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

所有的答案都是有效的,因为它工作得很好。但是,浏览器也会在每次加载带有不同 URL 的图像时在缓存中创建另一个文件。因此,不要通过添加一些查询参数来更改 URL。

所以,我们能做的就是使用 cache.put更新浏览器缓存

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

我发现 Chrome 特别试图在图片的 URL 参数解决方案上变得更聪明。这种避免缓存的方法只能在 一些的时间段内工作。 我发现最可靠的解决方案是同时添加一个 URL 参数(例如时间戳或文件版本) 还有也会改变 URL 中图像文件扩展名的大小写。

<img src="picture.jpg">

变成了

<img src="picture.JPG?t=current_time">