有没有一种方法可以强制浏览器刷新/下载图片?

71

我遇到一个问题,用户报告说他们上传的图像没有成功,旧的图像仍然存在。经过仔细检查,新的图像已经被成功上传,只是它们的文件名与旧的相同。在上传时,为了SEO目的,我对图像进行了重命名操作。当用户删除图像后,原来的索引变得可用并被重复使用,因此会有相同的图像名称。

有没有一种方法(我认为可能会有一个meta标签可以实现)能够告诉浏览器不要使用缓存?

更好的解决方案是将图像重新命名为全新的名称。我将着手解决这个问题,但同时也提供了一个快速解决方案,即重新命名图像文件来代替重复利用原有名称的方式。


如果您想在缓存中更新而不是更改URL,请参考以下链接:https://dev59.com/ZHVC5IYBdhLWcg3w-mZO#70954519 - Ravi Singh
14个回答

163

在任意URL后添加查询字符串,例如唯一数字、时间、版本号等:

<img src="image.png?80172489074" alt="a cool image" />

由于URL不同,这将导致一个新的请求。


3
+1,但严格来说它并不是不同的文件名;-)文件名组件相同,但URL肯定不同。 - Michael Krelin - hacker
17
"Gubmo, unique is a bit too much, the best in this case would be to add file's mtime — this way it will only be reloaded when needed." 可以翻译为:"Gubmo,unique(唯一)这个词用得有点过了,最好的方法是加上文件修改时间(mtime)—— 这样只有在需要重新加载时才会重新加载。" - Michael Krelin - hacker
2
最好的方法是使用发布版本的版本号。 - rahul
1
@hacker:随机性并不能保证生成的两个值不会相同。但是唯一性确保了生成的两个值不相同。那么,该文件的修改日期不就是该文件的唯一数据吗? - Gumbo
1
@RobertSinclair 是的,完全正确。每个URL将独立缓存。这就是为什么你可以例如附加文件修改时间、版本号或文件哈希来强制进行“浏览器刷新”,如果底层文件发生更改的原因。 - knittl
显示剩余3条评论

25

这很困难。你确实希望图片被缓存,但一旦有新的可用的图片,你又不希望它们被缓存:

  • 使用过期头并将日期设为过去会阻止任何缓存。糟糕。
  • 添加一个“缓存破坏”参数?342038402可以解决问题,但也可能防止图像被缓存,这不是你想要的。糟糕。
  • 使用短(比如1小时)过期时间的过期头更好...一小时后,用户将看到图片,但您的Web服务器不必每次都提供服务。这是一种妥协,但什么时间有效?这实际上不可行。

解决方案?我能想到两个好的选项:

  • 研究 eTag 以及你使用它们的能力。它们是为这种情况设计的。浏览器将明确地询问您的服务器文件是否最新。如果你还没有它,你只需在 Apache 中打开它。
  • 为每个新图片创建一个新的 URL(并使用远期过期头)。这就是你正在努力完成的。

1
"添加“缓存破坏”...不好。" 这并不正确,它会强制浏览器缓存该图像的特定版本,因此image.png?342038402将被缓存为该版本。如果您有具有不同查询字符串的相同图像,则是不好的。 - michaelmol
michaelmol,但是每次更改图像时,您将不得不手动创建一个新的随机数?我知道您不希望它永远不缓存。使用这种技术,我看不到一种方法只上传具有相同名称的图像到其位置,并使用新的随机数以编程方式更新路径。 - AZ Chad
2
如果使用 PHP,您可以结合“缓存打破”参数使用PHP的filemtime(string $filename)函数。这样,每次请求的文件发生更改时,该文件的URL都会更改。(感谢黑客Michael Krelin)。 - RicoBrassers
1
无论您是否更改与图像URL相关联的值,都没有关系。一旦在末尾有任何内容,您的浏览器将从服务器获取新的副本(即使该值未更改)。这就是为什么它很糟糕! - Ben Bozorg

11

我最喜欢的PHP解决方案:

<img src="<?php echo "image.jpg" . "?v=" . filemtime("image.jpg"); ?>" />

每次文件更改都会创建该文件的新版本


我认为你的引号有些不平衡。看起来你在下面发布了相同/类似的答案,引号是正确的?也许可以编辑一下以匹配它们... - beroe

5
将当前日期时间添加到图像的src中:
<img src="yourImage.png?v=<?php echo Date("Y.m.d.G.i.s")?>" />

2
你可以关闭缓存,因为你的例子会迫使浏览器每次下载文件(因为时间戳每秒都会改变)。我建议删除结尾的“i”和“s”,这样时间戳每小时改变一次。这样浏览器每小时仅需下载一次图片。 "Y.m.d.G" - Shumoapp

4
使用随机数并将其附加到图像文件名中。
<img src="image.jpg?<?=rand(1,1000000)?>">

1
尝试使用文件的最后修改日期,这样只有在文件被更改时才需要下载新文件。 - Gianluca Demarinis

4
你可以在 <meta> 标签中使用 http-equiv ,告诉浏览器不要使用缓存(或者更好的方式是以某种定义好的方式使用它),但最好的方法是配置服务器以发送正确的 http cache 头部信息。请查看关于缓存的文章
但是,一些浏览器可能不支持所有的 http 标准,但我相信这是正确的方法。

相反,使用解决方法支持浏览器对这些想法的不良支持 ;) 我理解你的观点,并且我知道进一步讨论通常没有意义 - 有些东西“更好”,但不能按预期工作,有些东西“更差”,但确实可以工作。这完全取决于 Web 开发人员,他/她会选择什么。 - samuil

4

另一个强大的解决方案:

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

打印出路径上的“最后修改时间戳”。

新版本-->下载新镜像

相同版本-->使用缓存的镜像


2

通过调整HTTP头,您可以控制缓存行为。

将过期时间设置为过去会强制浏览器不使用缓存版本。

Expires: Sat, 26 Jul 1997 05:00:00 GMT

您可以参考RFC获取更多细节。


1
RageZ,这里的问题是你还需要让某些浏览器开发者参考RFC。 - Michael Krelin - hacker
是的,但至少要有相关的文档。在实现故障时有点棘手... - RageZ
我完全支持正确的实现,但我们必须面对现实。 - Michael Krelin - hacker
大多数浏览器都正确实现了这一点,回到IE6时代那是很丑陋的,但现在应该没问题了。 - RageZ
如果浏览器从其HTTP缓存中请求图像,则不应使用缓存版本。但是,如果浏览器具有图像的内存副本,则有时会仅使用该副本而不查询其缓存。向图像URL添加唯一的片段标识符以确保它不使用图像的内存副本。 - Doin

2
如果您查看浏览器和服务器之间交换的数据,您会发现浏览器会为图像发送HTTP HEAD请求。结果将包含修改时间(但不包括实际图像数据)。确保如果服务器上的图像发生更改,则此时间也会更改,浏览器应重新下载图像。

根据浏览器的不同,一种低延迟的技术是在GET请求中使用If-Modified-Since头部。结果仍然相同 - 确保正确报告文件的修改时间,以便缓存失效工作正常。 - Toby Speight

2

在PHP中,您可以使用这个技巧

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

time()函数显示当前时间戳。每次页面加载都是不同的。因此,这段代码欺骗了浏览器:它读取另一个路径,并且“认为”自上次用户访问该站点以来图像已更改。因此,它必须重新下载它,而不是使用缓存文件。


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