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

时间:2008-09-24 12:19:18

标签: html image caching browser-cache image-caching

背景

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

问题

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

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

问题是显示的图片不会刷新。即使数据库保存正确的图像,仍会显示旧图像。我已将其缩小到图像在Web浏览器中缓存的事实。如果管理员在Firefox /资源管理器/ Safari中点击了RELOAD按钮,则所有内容都会被刷新,新图像就会出现。

我的解决方案 - 不工作

我试图通过写一个过去很远的日期的HTTP Expires指令来控制缓存。

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

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

但是,这也不起作用。

备注

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

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

问题

有没有办法强制网页浏览器不从这个页面缓存内容,甚至不是图像?

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

18 个答案:

答案 0 :(得分:168)

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

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

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

的Python生成时间

答案 1 :(得分:45)

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

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

HTTP RFC说的是什么:

Cache-Control: no-cache

但这不能很好地发挥作用:)

答案 2 :(得分:18)

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

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

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

答案 3 :(得分:14)

我会用:

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

其中&#34; 20130910043254&#34;是文件的修改时间。

  

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

我认为有两种类型的简单解决方案:1)首先想到的那些(直截了当的解决方案,因为它们很容易想出来),2)你在思考之后最终得到的那些(因为它们很容易使用)。显然,如果你选择考虑事情,你将永远不会受益。但我相信,第二种选择被低估了。试想为什么php如此受欢迎;)

答案 4 :(得分: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上的无缓存标头或随机数应该足够了,但是因为我们想要防弹..

答案 5 :(得分: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:不起作用,我必须清除历史&amp;网络数据。

关于SAFARI / iPad的任何想法?

答案 6 :(得分:5)

使用 Class =&#34; NO-CACHE&#34;

示例html:

int    is_prime(unsigned long long int num) { 
    if (num == 2) {
        return (1);                /* Special case */
    }
    if (num % 2 == 0 || num <= 1) {
         return (0);
    }
    unsigned long long int z = 3;  /* We skipped the all even numbers */
    while (z < num) {              /* Do a single test instead of your redundant ones */
        if ((num % z) == 0) {
            return 0;
        }
        z += 2;                     /* Here we go twice as fast */
    }
return 1;
}

jQuery的:

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

的javascript:

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

结果: SRC =&#34;影像/ img1.jpg&#34; =&gt; src =&#34; images / img1.jpg?a = 0.08749723793963926&#34;

答案 7 :(得分:4)

  

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

更改此问题,您已修复问题。我使用时间戳,就像上面提出的解决方案一样:图像 - &lt; timestamp&gt; .jpg

据推测,通过保持图像的相同文件名可以避免任何问题,但是你没有说出它们是什么。

答案 8 :(得分:4)

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

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

起初。

但是,如果你添加 cache = none 参数(这是静态的&#34;无&#34;字),它不起作用,浏览器仍然从缓存加载。< / p>

解决这个问题的方法是:

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

修改

虽然上面的解决方案运行正常,但有时您希望提供缓存版本UNTIL文件已更改。 (使用上面的解决方案,它会完全禁用该图像的缓存) 因此,要从浏览器UNTIL提供缓存图像,图像文件的使用会发生变化:

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

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

答案 9 :(得分:2)

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

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

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

答案 10 :(得分:2)

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

将始终为您的文件提供一个随机数字并停止缓存

答案 11 :(得分:1)

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

如果该时间戳对应于图像的上次更新时间,则可以在需要时缓存并在恰当的时间提供新图像。

答案 12 :(得分:1)

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

<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

答案 13 :(得分:1)

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

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

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

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

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

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

答案 14 :(得分:1)

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

答案 15 :(得分:0)

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

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

一个简单的例子如下:

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

答案 16 :(得分:0)

我制作了一个PHP脚本,该脚本自动为HTML页面中的所有图像添加时间戳。您只需要在页面中包含此脚本即可。享受吧!

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

答案 17 :(得分:-2)

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

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