如何强制网页浏览器不caching图像
背景
我正在为两个公益网站编写和使用一个非常简单的基于CGI的(Perl)内容pipe理工具。 它为网站pipe理员提供HTML表单,用于填充字段(date,地点,标题,描述,链接等)的事件并保存。 在这种forms下,我允许pipe理员上传与事件相关的图像。 在显示表单的HTML页面上,我也显示了上传图片的预览(HTML img标签)。
问题
当pipe理员想要更改图片时,会发生问题。 他只需点击“浏览”button,select一个新的图片,然后按确定。 这工作正常。
一旦图像上传,我的后端CGI将处理上传并正确地重新加载表单。
问题是显示的图像没有刷新。 即使数据库保存了正确的图像,旧图像仍然显示。 我已经缩小到在networking浏览器中的图像caching的事实。 如果pipe理员点击Firefox / Explorer / Safari中的RELOADbutton,则所有内容都会刷新并显示新图像。
我的解决scheme – 不工作
我正试图通过写一个HTTP Expires指令来控制caching,这个指令的date很远。
Expires: Mon, 15 Sep 2003 1:00:00 GMT
请记住,我在pipe理方面,我不在乎,如果页面需要更长的时间来加载,因为他们总是过期。
但是,这也不pipe用。
笔记
上传图片时,其文件名不保存在数据库中。 它被重新命名为Image.jpg (当使用它时简单的事情)。 当用新的图像replace现有的图像时,名称也不会改变。 只是图像文件的内容发生变化。
networking服务器由主机服务/ ISP提供。 它使用Apache。
题
有没有办法强制网页浏览器不caching从这个网页的东西,甚至没有图像?
我正在与select实际“保存文件名”与数据库杂耍。 这样,如果图像被改变,IMG标签的src也会改变。 但是,这需要在整个网站上进行很多更改,如果我有更好的解决scheme,我宁愿不要这样做。 此外,如果上传的新图像具有相同的名称(例如,图像被稍微拍摄并重新上传),这仍然不起作用。
阿明·罗纳彻有正确的想法。 问题是随机串可能会碰撞。 我会用:
<img src="picture.jpg?1222259157.415" alt="">
其中“1222259157.415”是服务器上的当前时间。 (注:我用Python的time.time()来生成)
简单的修复:将随机查询string附加到图像:
<img src="foo.cgi?random=323527528432525.24234" alt="">
什么HTTP RFC说:
Cache-Control: no-cache
但那不是那么好:)
我使用PHP的文件修改时间函数 ,例如:
echo <img src='Images/image.png?" . filemtime('Images/image.png') . "' />";
如果更改图像,则使用新图像而不是caching的图像,原因是具有不同的修改时间戳。
我会用:
<img src="picture.jpg?20130910043254">
其中“20130910043254”是该文件的修改时间。
上传图片时,其文件名不保存在数据库中。 它被重新命名为Image.jpg(当使用它时简单的事情)。 当用新的图像replace现有的图像时,名称也不会改变。 只是图像文件的内容发生变化。
我认为有两种简单的解决scheme:1)首先想到的(简单的解决scheme,因为它们很容易拿出来),2)在思考之后最终得到的东西(因为它们很容易使用)。 显然,如果你select思考问题,你不会总是受益。 但是我相信,第二种select是相当低估的。 想想为什么php
如此受欢迎;)
你可以写一个服务图像的代理脚本 – 这是多一点工作。 东西喜欢这个:
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 MYH: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中的no-cache标头或随机数应该是足够的,但是因为我们想要防弹。
我是一个新的编码器,但这是我想出来的,以阻止浏览器caching并保持到我的摄像头视图:
<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的任何想法?
上传图片时,其文件名不保存在数据库中。 它被重新命名为Image.jpg(当使用它时简单的事情)。
改变这一点,你已经解决了你的问题。 我使用时间戳,与上面提出的解决scheme一样: Image- <timestamp> .jpg
据推测,无论你通过保持图像的相同文件名来避免什么问题都可以被克服,但你不会说出它们是什么。
我检查了networking上的所有答案,最好的答案似乎是:(实际上不是)
<img src="image.png?cache=none">
首先。
但是,如果添加cache = none参数(这是静态的“无”字),则不起任何作用,浏览器仍然从caching中加载。
解决这个问题的方法是:
<img src="image.png?nocache=<?php echo time(); ?>">
你基本上添加unix时间戳使参数dynamic和没有caching,它的工作。
但是,我的问题有点不同:我正在加载生成的PHP图表图像,并用$ _GET参数控制页面。 当URL GET参数保持不变时,我希望从caching中读取图像,并且在GET参数更改时不caching。
为了解决这个问题,我需要散列$ _GET,但是因为它是数组,所以解决scheme是:
$chart_hash = md5(implode('-', $_GET)); echo "<img src='http://img.dovov.commychart.png?hash=$chart_hash'>";
编辑 :
虽然上面的解决scheme工作得很好,但有时候您想要提供caching的版本,直到文件被更改。 (使用上面的解决scheme,它会完全禁用该映像的caching)因此,要从浏览器提供caching的映像直到映像文件的使用发生更改:
echo "<img src='http://img.dovov.commychart.png?hash=" . filemtime('mychart.png') . "'>";
filemtime()获取文件修改时间。
有可能在你和客户端之间performance不好的透明代理,完全保证图片不会被caching的唯一方法就是给它们一个唯一的uri,比如把一个时间戳标记为查询string或者作为path。
如果该时间戳对应于图像的上次更新时间,则可以在需要时caching,并在恰当的时间提供新图像。
我假设原来的问题是与一些文本信息存储的图像。 因此,如果您在生成src = … url时有权访问文本上下文,请考虑使用图像字节的CRC32而不是无意义的随机或时间戳。 然后,如果显示大量图像的页面,则只有更新的图像将被重新加载。 最后,如果CRC存储是不可能的,则可以在运行时计算并附加到URL。
你的问题是,尽pipeExpires:
头,你的浏览器在更新之前重新使用它的内存副本,而不是检查它的caching。
我有一个非常类似的情况上传产品图像在pipe理后端为类似商店的网站,在我的情况下,我决定最好的select是使用JavaScript来强制刷新图像,而不使用任何其他人的URL修改技术这里已经提到了。 相反,我把图像的URL放到一个隐藏的IFRAME中,在IFRAME的窗口上调用了location.reload(true)
,然后在页面上replace了我的图像。 这会强制刷新图像,而不仅仅是在我正在访问的页面上,而且还会在我访问的任何后续页面上进行刷新 – 没有客户机或服务器必须记住任何URL查询string或碎片标识符参数。
我在这里回答了一些代码。
从我的angular度来看,禁用图像caching是一个坏主意。 完全一样。
这里的根本问题是 – 如何强制浏览器更新图像,当它已经在服务器端更新。
再次,从我个人的angular度来看,最好的解决办法是禁止直接访问图像。 而是通过服务器端filter/ servlet /其他类似的工具/服务访问图像。
在我的情况下,这是一个rest服务,返回的形象,并附加ETag作为回应。 服务保持所有文件的散列,如果文件被改变,散列被更新。 它适用于所有现代浏览器。 是的,实施它需要时间,但这是值得的。
唯一的例外是favicons。 由于某些原因,这是行不通的。 我无法强制浏览器从服务器端更新caching。 ETags,caching控制,过期,Pragma标题,没有任何帮助。
在这种情况下,在url中添加一些随机/版本参数似乎是唯一的解决scheme。