用Etag实现php-GD创建的图片缓存

转帖,这篇文章是我去年写的。转到新博客里来。。。。。

网站把图片存在数据库里,有的时候比较方便,有的时候就有点麻烦了。。。比如这些图片不经常更改的时候。。

数据库里的图片一般用php的GD库取出来。然后声明图片格式:

1
header('Content-Type: image/png'); 

进行适当的操作之后,再用

1
imagepng($im); 

输出到浏览器里面。

这本来是一种很好的方式。。可是我马上发现了,当我刷新页面的时候,图片重新载入了。

然后图片非常大,于是我的网站变得很慢 。

php生成的图片不能像正常放在服务器里的图片一样缓存么?

我在尝试了之后终于找到了解决方案:

HTTP/1.1里面有个东西叫做Etag,定义为“被请求变量的实体值”。就是指标识客户端正在请求的数据的一个标记(Token一样的东西)。
使用

1
header('Etag:'.$ETag); 

来设置Etag,其中变量$ETag为自定义的Etag的值。

在HTTP请求里面,客户端会带上If-None-Match,内容就是服务器发来的Etag。然后我们只要用php比对一下是不是相同,如果相同就返回304 Not Modified,如果不同就继续返回数据。

HTTP并没有规定Etag应该是什么样的,于是我们可以自己定义Etag的值,只要保证每次更改Etag都不同就可以了。。常见的是使用md5这样的哈希散列值。

php代码非常简单:

1
2
3
4
5
6
7
$ETag="你自己的Etag"; 
if($_SERVER['HTTP_IF_NONE_MATCH']==$ETag){
header('HTTP/1.1 304 Not Modified'); //返回304,告诉浏览器调用缓存
exit();
}else{
header('Etag:'.$ETag);
};

这一段放在最开头就好了。。。

当然其实我不是把它放在最开头的,我用GD的另一个目的是反外链,于是我把它放在了我的反外链代码后面。如果是外链,直接返回替换的图片,然后才进入这一段代码判断是不是可以调用缓存。

写到这里,这一篇就应该结束了,不过我还有两点需要补充:

第一点是——Etag和URL有关,不同的URL上即使是相同的Etag也没有任何意义。只有URL相同,Etag也相同才会调用缓存。这就要求我们不能用post传参。。。

但是Etag并没有说一定只能用在特定的文件上面,Etag什么都可以用,包括图片,音乐,CSS,JS,HTML本身,都可以有一个Etag,然后用Etag来控制缓存。

第二点是——

在我给我的图片输出页加上这样的代码,然后测试的时候,我发现服务器返回的并不是304,而是200。HTTP header信息里面分明写着:

1
Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0 

坑爹嘛这是。检查了Apache设置,php设置和.htaccess设置都没发现这东西是从哪里带上的。。。最后我发现。。其实是因为这个php一开头第一个语句:

1
session_start(); 

php会话信息默认带no-cache头。。

于是如果你的php非要带session_start();,就像我一样,一定要从SESSION里面拿gid去查询数据库。就要在一开头(session_start前面)加上:session_cache_limiter('public');了。

1
2
3
<?php 
session_cache_limiter('public');//设置session缓存
session_start();//读取session

现在是真正的结束了。。

PS。。。。PHP的GD库真是个好东西。。。