昨天,后端给我提了一个问题:他更换了CDN上的图片,但是他打开小程序来看,还是旧图片,他尝试过删除小程序,重新进,还是旧图片。
我第一反应是:“你有没有清CDN缓存?“
他说:”我在阿里云CDN控制台刷新缓存了,都两三天了,还是旧图片。”(后来我看过刷缓存记录,一天前刚刷的,他夸张了。。。)
我说:“那不应该呀,CDN刷新缓存,5分钟就生效了。”
当即拿出我的手机,进小程序看了一下,是新的图片。
他赶紧拿出他的手机给我看,果然,不管试多少次,都是旧图片。
我说:”你这个图片是文件名没改,请求地址没变,被客户端缓存了。但是这个没关系,你这个图片基本上不会换,这次换图,在我手机上已经验证了是没问题的,并且图片所在的模块是新增的,目前还没发布,用户没有访问记录,他们手机上没有旧图的缓存,发布后,用户手机上都会看到新图片,你手机上的旧图只是测试的时候访问过,就被缓存了。“
但是,他不同意,说:“万一后面又要换图了,那怎么办?”
虽然他说的万一概率很小,但是不怕一万就怕万一。我只好跟他说:”那行,我处理下“
很简单嘛,改文件名就好了。但是他这些图片名称都是按商品类目名称批量生成的,而且如果改文件名,他要改代码,我也要改代码。
那就不改文件名,在图片地址后面加查询字符串嘛,哪张图片变了,就把它地址后面的查询字符串改一下,比如?v=1
、?v=2
,这样他就不用改代码了,但是前端还是要改代码,每次图片变了,我得改小程序里引用这张图片地址后面的查询字符串,这还要重新发布。
这个方法太笨了,直接在图片地址后面生成时间戳不就好了,如:${url}?ts=${new Date().getTime()}
。但是这样图片每次都会重新请求,性能会产生问题。
那为什么图片会被缓存那么久了?怎样才能让缓存失效了?
打开控制台看下请求,如下:
从控制台里可以看到,图片响应是from disk cache
,说明图片资源被缓存到磁盘了,disk cache
是强缓存,它是持久存储。但是disk cache
是会严格根据HTTP
头信息中的缓存控制字段来判定哪些资源可以缓存,缓存多久的。我们可以在HTTP
响应头里设置Cache-Control
或Expires
来告诉小程序,这个图片应不应该被缓存,如果缓存,应该缓存多久。
可以看到,上图中,我的图片响应头里是没有Cache-Control
和Expires
的,所以当你没有在响应头里设置缓存策略时,小程序会强缓存你的图片,而且会缓存很久很久;
在我的需求里,我这个图片不会经常变动,就算变了,一天后生效也是OK的,所以我给这个图片的响应头加上Cache-Control: max-age=86400
就可以了,max-age
以秒为单位,86400
即24小时
。
这样设置以后,用户第一次访问图片会被小程序缓存,24小时内用户再次访问此图片,小程序不会发出网络请求,而是直接从磁盘缓存里读取。24小时以后,缓存过期,用户再次访问此图片时,小程序会发出请求从服务器获取最新图片。
我的图片是放在阿里云上的,应该如何设置响应头呢?
虽然图片是从CDN请求,但是我们CDN源站设置的是阿里云对象存储OSS的Bucket
域名,对象存储OSS可以设置资源的HTTP
响应头。
登录阿里云,进入对象存储OSS,找到资源所在Bucket
-文件管理,进入资源所在目录,资源列表右边“更多”里有“设置HTTP头“,除了可以设置单个资源的HTTP头,还可以选择当页所有资源,批量设置HTTP头。点击“设置HTTP头“,在“Cache-Control"字段填上值保存就可以了,如下图:
除了刚才说的强缓存,还有一种协商缓存策略,再看我之前贴的那个图片响应的图,响应头里虽然没有Cache-Control
和Expires
,但是有ETag
和Last-Modified
,上面说当disk cache
过期后,小程序会重新向服务端发起请求,此时客户端会在请求头带上上一次图片响应的ETag
和Last-Modified
,分别放在If-None-Match
和If-Modified-Since
里,服务器接受到这两个字段后,会和当前资源比较,如果ETag
变了,或者资源修改时间大于上次修改时间,将返回新资源,否则返回304
告诉客户端,资源没有变化。