原理
当浏览器第一次向服务端发送请求时,服务端会返回一个带有Last-Modified: Sat, 02 Dec 2017 04:03:14 GMT
字段的响应头,该字段表明所请求的文件最新修改时间;当浏览器下一次向服务端发送请求时,请求头会带上 If-Modified-Since: Sat, 02 Dec 2017 04:03:14 GMT
字段,该字段的值是上一次服务器 Last-Modified
返回的值,服务器接收到请求后会根据 If-Modified-Since
值进行判断,如果该值小于服务器文件的值则返回新的文件,否则就告诉浏览器使用缓存文件。
举个例子,当浏览器第一次请求时
当浏览器第二次发送请求时
思路
- 服务器对浏览器请求的请求头
if-modified-since
进行判断,若不存在,则发送文件以及请求头Last-Modified
,说明该文件的修改时间 - 若存在请求头
if-modified-since
,将请求头if-modified-since
的值与文件修改时间进行比较 - 如果请求头
if-modified-since
的值小于文件修改时间,则发送文件以及请求头Last-Modified
,说明该文件的修改时间 - 如果请求头
if-modified-since
的值等于或大于文件修改时间,则返回304状态码,告知浏览器读取缓存文件
实例
没有实现缓存时:
此时每次请求服务器都会重新发送文件(观察状态码)
实现缓存之后:
const http=require('http');
const fs=require('fs');
const url=require('url');
http.createServer( (req, res) => {
let {pathname} = url.parse(req.url);
// 获取文件日期
fs.stat(`.${pathname}`, (err, stat) => {
if(err) {
res.writeHeader(404);
res.write('Not Found');
res.end();
}else {
if (req.headers['if-modified-since']) {
// 浏览器 if-modified-since 字段值
let oDate = new Date(req.headers['if-modified-since']);
let time_client = Math.floor(oDate.getTime() / 1000);
// 服务端文件最新修改时间
let time_server = Math.floor(stat.mtime.getTime() / 1000);
if (time_client < time_server) {
// 浏览器缓存文件的修改时间小于服务端文件修改时间,发送文件
sendFileToClient();
}else {
// 浏览器缓存文件的修改时间等于或大于服务器文件的修改时间
// 发送 304 状态码,告知浏览器从缓存中读取数据
res.writeHeader(304);
res.write('Not Modified');
res.end();
}
}else {
// 浏览器是第一次请求该文件,不存在 if-modified-since 字段
// 从服务器端读取文件
sendFileToClient();
}
function sendFileToClient() {
let rs = fs.createReadStream(`.${pathname}`);
// 设置请求头 Last-Modified 字段,值为该文件最新修改时间
res.setHeader('Last-Modified', stat.mtime.toGMTString());
// 输出
rs.pipe(res);
rs.on('error', err => {
res.writeHeader(404);
res.write('Not Found');
res.end();
});
}
}
})
}).listen(1337);