概述
http缓存的实现方式有
1.Cache-Control/Expires。Cache-Control的Max-age属性设置的绝对过期时间,单位为秒。比如max-age:300,表示文件会缓存在本地300s之后过期.Expires代表的是绝对缓存时间,表示缓存在指定的时间点过期,格式为Expires:Thu,20 Apr 2019 20:00:00 GMT。这个时间是由服务器设置的,但本地浏览器和服务器之间可能存在时差,所以有时这个值不准确。当Cache-Control的max-age和expires同时存在时,max-age的优先级高。
2.Last-Modify/If-Modify-Since。这两个是成对存在的,代表文件最后的修改时间, Last-Modified 是由服务器往浏览器发送的 HTTP头,每次发送的值会写进浏览器的If-Modify-Since中。If-Modified-Since是由浏览器往服务器发送的头,浏览器每次请求服务器都会带上这个头。
3.Etag/If-None-Match.这也是成对存在,功能和(2)类似。不过在开发中往往是对服务器的文件求Hash,并把求出的hash值赋给Etag。当Etag和Last-Modify同时存在的时候服务端对两种都要校验。
4.Etag和Last-Modify对比。
(a)、服务器周期性动态生成的文件,他的内容并不改变,但修改时间变了这时我们应该用Etag
(b)、对于修改非常频繁的文件,Etag可以检测到秒级以下,If-Modified-Since只能能检查到s级的。
5.Last-Modified,Etag,Expire 混合使用
(a)、通常 Last-Modified,Etag,Expire 是一起混合使用的,特别是 Last-Modified 和 Expire 经常一起使用,因为 Expire 可以让浏览器完全不发起 Http 请求,而当浏览器强制 F5 的时候又有 Last-Modified ,这样就很好的达到了浏览器段缓存的效果。
(b)、Etag 和 Expire 一起使用时,先判断 Expire ,如果已经过期,再发起 Http 请求,如果 Etag 也过期,则返回 200 响应。如果 Etag 没有过期则返回 304 响应。
(c)、Last-Modified,Etag,Expires 三个同时使用时。先判断 Expire ,然后发送 Http 请求,服务器先判断 last-modified ,再判断 Etag ,必须都没有过期,才能返回 304 响应
实现
上面说了那么一大堆,现在用node来实现一个Last-Modify/If-Modify-Since的缓存
一、实现一个简单服务器
const http = require('http');
const fs = require('fs');
const urlLib = require('url');
http.createServer((req, res) => {
let { pathname } = urlLib.parse(req.url, true);
if (pathname != '/index.html') {
res.writeHead(302, { 'Location': 'http://localhost:9090/index.html' });
res.end();
} else {
let rs = fs.createReadStream(`./${pathname}`);
rs.pipe(res);
}
}).listen(9090);
console.log('server start at http://localhost:9090/index.html')
可以看到无论怎么刷新返回的Status都是200,并且响应头中也没有If-Modify-Since这项
二、读取文件的修改时间并设置服务端Last-Modified
http.createServer((req, res) => {
let { pathname } = urlLib.parse(req.url, true);
if (pathname != '/index.html') {
res.writeHead(302, { 'Location': 'http://localhost:9090/index.html' });
res.end();
} else {
+ fs.stat(`./${pathname}`, (err, stat) => {
if (err) {
console.log('请在根目录下创建一个index.html文件');
} else {
+ if (req.headers['if-modified-since']) {
+ console.log(req.headers['if-modified-since'])
}
+ res.setHeader('Last-Modified', stat.mtime.toGMTString());
let rs = fs.createReadStream(`./${pathname}`);
rs.pipe(res);
}
})
}
}).listen(9090);
现在我们可以看到浏览器的请求头多了一个If-Modified-Since,且服务器可以获取到该值
三、用If-Modified-Since的值判断服务器文件是否过期
fs.stat(`./${pathname}`, (err, stat) => {
if (err) {
console.log('请在根目录下创建一个index.html文件');
} else {
if (req.headers['if-modified-since']) {
+ let date = new Date(req.headers['if-modified-since']);
+ let client_time = Math.floor(date.getTime() / 1000);
+ let server_time = Math.floor(stat.mtime.getTime() / 1000);
+ if (client_time < server_time) {
+ sentFileToClient();
+ } else {
+ res.writeHead(304);
+ res.write('Not Modified');
+ res.end();
}
} else {
sentFileToClient();
}
function sentFileToClient() {
res.setHeader('Last-Modified', stat.mtime.toGMTString());
let rs = fs.createReadStream(`./${pathname}`);
rs.pipe(res);
}
}
})
我们可以看到第一次请求status是200,然后刷新都是304更改文件后status又变成了200代码地址:github.com/fanxuewen/e…