为某些图片禁用缓存

137

我使用PHP库生成了一些图片。

有时候浏览器无法加载新生成的文件。

我如何仅针对动态创建的图像禁用缓存?

注意:我必须一直使用相同的名称来创建这些图像。

13个回答

281

解决这个问题的常见且简单的方案,感觉像是一个技巧,但相当具有可移植性,那就是为每个请求动态图像添加一个随机生成的查询字符串。

例如:

<img src="image.png" />

将会变成什么

<img src="image.png?dummy=8484744" />

<img src="image.png?dummy=371662" />

从Web服务器的角度来看,访问的是同一个文件,但从浏览器的角度来看,无法执行任何缓存操作。

随机数生成可以在服务器上在提供页面时进行(只需确保页面本身未被缓存...),也可以在客户端上使用JavaScript进行。

您需要验证您的Web服务器是否能够处理此技巧。


104
使用数据更改的时间戳或反映数据版本号,而非随机数字。 - lhunath
4
如何强制网络浏览器不缓存图片?您可以通过在图像的URL中添加唯一的查询字符串参数来强制浏览器重新加载图像,例如时间戳或随机数。这将导致浏览器认为该图像是新的,并从服务器重新请求。例如,假设您有一个名为“image.jpg”的图像文件,您可以将其URL更改为以下内容:http://example.com/images/image.jpg?t=123456其中“t=123456”是根据当前时间生成的时间戳或随机数。每次您更改查询字符串参数时,浏览器都会认为它是另一个图像,并重新加载它。 - Philibert Perusse
23
请注意:实际上,你并不能阻止浏览器缓存图片,你只是防止查看已缓存的图片。为你的图片应用适当的标头是我个人认为最好的方法(请参考lhunath的解决方案)。这样做的好处是,你可以避免把不想缓存的图片填满缓存,从而占用了本来可以被有用地缓存的其他东西的缓存空间。 - Jos
我相信这篇文章解释了这种行为的原因。 - Metalcoder
2
这并不起作用,图像需要以另一种方式刷新(通常在图像裁剪时,图像保持不变)。 - Ben
显示剩余2条评论

47

通过HTTP头,可以控制浏览器缓存策略。请记住,它们只是一个提示。由于浏览器在这个(以及其他任何)领域都非常不一致,因此您需要多个头来在各种浏览器上获得所需的效果。

header ("Pragma-directive: no-cache");
header ("Cache-directive: no-cache");
header ("Cache-control: no-cache");
header ("Pragma: no-cache");
header ("Expires: 0");

2
我无法仅针对一个图像禁用缓存(页面中的特定图像)? - dole doug
8
@Thorpe:它适用于HTTP响应。 响应中包含的内容并不重要,无论是图像数据、HTML数据还是其他任何数据类型。如果它没有起作用,你可能没有正确操作。检查响应的HTTP头部是否被正确分配即可。 - lhunath
3
请注意,添加标题对于已经被缓存的图像是无效的,因为浏览器甚至不会向服务器请求它们,因此永远不会看到标题。但是,对于您添加标题后发出的任何图像请求,标题将起作用。 - lhunath
@Ihunath,我该如何为特定的图像设置标题?我真的不知道在HTML文档中应该如何实现。 - Torsten Barthel
2
这个解决方案是为程序员而设计的,而不是网页设计师。我想指出这一点,因为除非他们自己用编程语言生成图像,否则不能仅仅打开图像并添加头文件,这似乎让评论者感到困惑。 - Bruce
显示剩余3条评论

18

方案1并不理想。虽然它可以工作,但将hacky随机或带时间戳的查询字符串添加到图像文件的末尾会使浏览器重新下载和缓存每个图像的每个版本,每次加载页面时都会发生,无论图像在服务器上是否已更改。

方案2毫无用处。向图像文件添加nocache标头不仅非常难以实现,而且完全不切实际,因为它要求您提前预测何时需要它,第一次加载您认为可能在未来的某个时间点可能更改的任何图像。

输入Etags...

我发现的绝对最好的方法是在您的图像目录中使用ETAGS文件内的.htaccess文件。以下内容告诉Apache在图像文件标头中发送唯一的哈希值给浏览器。当图像文件被修改时,此哈希值仅会更改一次,并且此更改将触发浏览器在下一次请求图像时重新加载图像。

<FilesMatch "\.(jpg|jpeg)$">
FileETag MTime Size
</FilesMatch>

1
这个解决方案是否适用于以前缓存的文件? - christian

17

如果您需要在浏览器中使用JavaScript动态执行此操作,这里是一个示例...

<img id=graph alt="" 
  src="http://www.kitco.com/images/live/gold.gif" 
  />

<script language="javascript" type="text/javascript">
    var d = new Date(); 
    document.getElementById("graph").src = 
      "http://www.kitco.com/images/live/gold.gif?ver=" + 
       d.getTime();
</script>

15

我检查了所有答案,最好的答案似乎是这个(但它并不是最好的):

<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() 函数用于获取文件的修改时间。


?cache=none 只在无痕模式下有效,在普通标签页中无效。 - Chandan Y S

6

我知道这个话题很老,但它在谷歌上排名很高。我发现将此放在您的页眉中效果很好;

<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">

1
现代浏览器不幸地忽略了这些指令,因此这个解决方案可能只在某些浏览器上有效,而且它会禁用所有缓存,而不仅仅是特定的图像。 - ZioCain

5

我正在寻找一个解决方案,上面的答案在我的情况下没有用(而我声望不足以对它们进行评论)。结果发现,至少对于我的用例和我使用的浏览器(Mac上的Chrome),唯一似乎能防止缓存的是:

Cache-Control = 'no-store'

为了完整性,我现在使用了 'no-cache, no-store, must-revalidate' 这三个指令。

因此,在我的情况下(在 Python 的 Flask 中动态生成图像),我必须按照以下步骤进行,以便能够在尽可能多的浏览器中工作......

def make_uncached_response(inFile):
    response = make_response(inFile)
    response.headers['Pragma-Directive'] = 'no-cache'
    response.headers['Cache-Directive'] = 'no-cache'
    response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
    response.headers['Pragma'] = 'no-cache'
    response.headers['Expires'] = '0'
    return response

只需在响应中添加“no-store”就足以让它对我起作用了。 - scourge192
不仅仅是在Chrome上,Firefox也是如此。看起来这已经成为了一种标准:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control(请参见“_Preventing caching_”部分)。 - Gino Mempin

3
改变图片源是解决方案。您可以通过向图像添加时间戳或随机数来实现这一点。
更好的方法是添加数据的校验和,例如图像所表示的数据。这样可以在可能的情况下启用缓存。

2
让我们在这堆解决方案中再添加一个解决方案。

在末尾添加一个唯一的字符串是一个完美的解决方案。
example.jpg?646413154

以下解决方案扩展了此方法,并提供了缓存功能和在图像更新时获取新版本的能力。
当图像更新时,filemtime 将会改变。
<?php
$filename = "path/to/images/example.jpg";
$filemtime = filemtime($filename);
?>

现在输出图片:
<img src="images/example.jpg?<?php echo $filemtime; ?>" >

1
那是我使用的原因是因为缓存有效性。 - gene

1
我遇到了这个问题,解决方法如下。
var newtags='<div class="addedimage"><h5>preview image</h5><img src="'+one+'?nocache='+Math.floor(Math.random() * 1000)+'"></div>';

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