首页 文章

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

提问于
浏览
105

背景

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

问题

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

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

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

我的解决方案 - 不工作

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

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

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

但是,这也不起作用 .

注意事项

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

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

问题

有没有办法强制Web浏览器不缓存此页面中的内容,甚至图像?

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

15 回答

  • 6

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

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

    其中“1222259157.415”是服务器上的当前时间 . (注意:我使用python的time.time()来生成)

  • 2

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

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

    HTTP RFC说的是什么:

    Cache-Control: no-cache
    

    但那不是很好:)

  • 161

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

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

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

  • 1

    我会用:

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

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

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

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

  • 6

    您可以编写一个代理脚本来提供图像 - 虽然这有点多了 . 有点喜欢这个:

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

  • 44

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

    <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的任何想法?

  • 0

    上传图像时,其文件名不会保留数据库 . 它被重命名为Image.jpg(在使用时简单地说出来) .

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

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

  • 4

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

  • 1

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

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

    首先 .

    但是,如果添加 cache=none 参数(这是静态"none" word),它不起作用,浏览器仍然从缓存加载 .

    解决这个问题的方法是:

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

    Edit

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

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

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

  • 3

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

    我有一个非常类似的情况在管理后端上传产品图像为类似商店的网站,在我的情况下,我认为最好的选择是使用javascript强制图像刷新,而不使用任何其他人的URL修改技术这里已经提到了 . 相反,我将图像URL放入一个隐藏的IFRAME,在IFRAME 's window, and then replaced my image on the page. This forces a refresh of the image, not just on the page I'上打开 location.reload(true) ,但也放在我访问的任何后续页面上 - 没有客户端或服务器必须记住任何URL查询字符串或片段标识符参数 .

    我在答案here中发布了一些代码 .

  • 0

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

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

  • 13

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

  • 1

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

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

  • 18

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

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

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

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

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

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

  • 4

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

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

相关问题