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


124

背景

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

问题

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

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

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

我的解决方案-不起作用

我试图通过写一个HTTP Expires指令来控制缓存,该指令的日期已经过去了。

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

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

但是,这也不起作用。

笔记

上载图像时,其文件名不保留在数据库中。它被重命名为Image.jpg(以简化使用时的处理)。用新图像替换现有图像时,名称也不会更改。只是图像文件的内容会更改。

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

有没有一种方法可以强制Web浏览器不缓存该页面中的内容,甚至不缓存图像?

我正在尝试使用数据库来实际“保存文件名”的选项。这样,如果更改了图像,IMG标签的src也将更改。但是,这需要对整个站点进行很多更改,如果我有更好的解决方案,我宁愿不进行更改。另外,如果上传的新图像具有相同的名称,这仍然将不起作用(例如,将图像进行了照片购物并重新上传)。


我认为这个问题可能需要重新编写,以删除我在撰写本文时放入其中的所有“现在无用的上下文”。我认为标题是正确的,也是最佳答案。但是问题最初是为了为多种答案提供很大的空间而写的,并且无法期待如此简单的解决方案。因此,对于那些想在那里找到答案的人来说,这个问题有些令人费解。
Philibert Perusse

Answers:


186

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

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

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


32
一个重要的补充是您永远不能强迫浏览器执行任何操作。您所能做的就是提出友好的建议。由浏览器和用户实际遵循这些建议。浏览器可以随意忽略它,否则用户可以覆盖默认值。
乔尔·科洪

8
Joel,最好在自己的答案中加上它。
epochwolf

1
只是一个想法,现在来参加聚会已经太晚了-但是由于您可以控制图像的更改,因此也许最好在更新图像时重命名图像,而不是添加查询字符串。因此:例如1222259157.jpg,而不是picture.jpg?1222259157。这样,就可以对其进行更新,但是在重新访问时会重新缓存。
danjah 2011年

43
与其添加服务器时间(这将完全避免缓存),不如不添加文件在“?”之后的最后修改时间。这样,图像将被正常缓存,直到下一次更改为止。
Doin 2014年

3
Doin的最后评论必须被否决!智能缓存确实很重要,为什么有人必须一次又一次地下载同一文件?
f.arcarese,2014年

46

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

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

HTTP RFC怎么说:

Cache-Control: no-cache

但这不能很好地工作:)


1
如何给Cache-Control:普通html <img>标签中没有缓存?
P Satish Patro

是否需要在响应标头中出现?
P Satish Patro

22

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

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

如果更改图像,则由于具有不同的修改时间戳,因此将使用新图像而不是缓存的图像。


大提示。如果修改时间更改(覆盖或更改),则将缓存和图像都重新发送到客户端。
Vasilis Lourdas 2013年

看到这里的另一个选择stackoverflow.com/questions/56588850/…–
drooh

绝对钉了它。很棒的小伙伴。
Khurram Shaikh,

太好了!只是一个额外的提示:<img src =“ images / image.png?<?php echo filemtime('images / image.jpg')?>”>
Rica Gurgel

16

我会用:

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

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

上载图像时,其文件名不保留在数据库中。它被重命名为Image.jpg(以简化使用时的处理)。用新图像替换现有图像时,名称也不会更改。只是图像文件的内容会更改。

我认为有两种简单的解决方案:1)首先想到的那些(直截了当的解决方案,因为它们很容易提出),2)经过深思熟虑后最终得到的解决方案(因为它们很容易用)。显然,如果您考虑问题,您将永远不会受益。但我认为,第二种选择相当低估。试想一下为什么php如此受欢迎;)


1
+1我认为这是一个比其他答案更好的主意,因为使用las修改时间,当图像中没有任何变化时,您的服务器将不会尝试向同一浏览器多次提供同一图像。每次有请求时提取图像的最后写入时间都需要一点开销,但是对于较大的图像,这比每次都必须返回图像更好,而当图像更改时,用户将拥有新的图像。谢谢您的补充。
塞缪尔

好吧,可以将修改时间以及图像的路径存储在数据库中。因此,开销可能甚至不那么重要。另一方面,如果这些图像是我们正在讨论的源代码的一部分,则可能还会缓存其修改时间。通过生成具有图像修改时间的脚本(例如images.php)。该脚本必须在每次提交时重新生成,并且消除了确定文件修改时间的开销。
x-yuri 2014年

@ X-より这就是为什么我选择加入到数据库中的这个更新的存储领域,而不是运行filemtime stackoverflow.com/questions/56588850/...
drooh

9

使用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”


简单,优雅,不需要服务器端渲染。不错!
Ahi Tuna

7

您可以编写用于提供图像的代理脚本-但是要多做一些工作。像这样:

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处使用无缓存标头或随机数就足够了,但是由于我们希望做到防弹。


除了Pragma不是响应标头之外,您的方法是一个很好的解决方案。
Piskvor于


6

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

<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:不起作用,我必须清除历史记录和Web数据。

关于SAFARI / iPad的任何想法?


4

上载图像时,其文件名不保留在数据库中。它被重命名为Image.jpg(以简化使用时的处理)。

对此进行更改,就可以解决问题。我使用时间戳,以及上面建议的解决方案:Image- <timestamp> .jpg

大概可以解决通过为图像保留相同的文件名而避免的任何问题,但是您无需说明它们是什么。


4

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

<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()获取文件修改时间。


2
filemtime解决方案也更好,因为md5需要大量的处理能力。
oriadam '17

2
花了几天时间尝试获取基于Chromium的应用程序以停止缓存图像。带时间回显的?nocache解决了该问题。谢谢!
伍迪

2

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

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

我在这里的答案中张贴了一些代码来做到这一点


2

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

总是会在末尾为您的文件提供一个随机数并停止缓存


值得注意的是,实施此解决方案会对每个请求的服务器造成压力,并且不一定能正常工作,因为浏览器可以缓存php页面,该页面包含在初始请求被缓存时生成的时间。仅当页面也具有动态查询字符串时,这才能可靠地工作。
德米特里

1

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

如果该时间戳对应于映像的最后更新时间,则可以在需要时进行缓存,并在适当的时间提供新映像。


1

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


或者,使用图像的最后修改时间而不是CRC,效果更好!
Doin 2014年

1

从我的角度来看,禁用图像缓存是一个坏主意。完全没有

根本问题是-在服务器端更新图像后,如何强制浏览器更新图像。

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

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

唯一的例外-网站图标。由于某些原因,它不起作用。我无法强制浏览器从服务器端更新其缓存。ETag,缓存控制,过期,Pragma标头,无济于事。

在这种情况下,似乎将一些random / version参数添加到url中是唯一的解决方案。


1

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

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

一个简单的例子看起来像这样:

通常,图像会被显示和缓存,但是如果用户按下按钮,则会向xhr请求发送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>


0

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

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

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

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

但是您希望它永远不会从缓存中提供图像。为此,如果页面不使用页面缓存,则可以在PHP或服务器端使用。

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

但是,它仍然无效。原因:浏览器缓存...最后但最有效的方法是本机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" alt="">

结果像这样

https://example.com/image.png?1582018163634
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.