We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
使用缓存的流程图 简单来讲,本地没有文件时,浏览器必然会请求服务器端的内容,并将这部分内容放置在本地的某个缓存目录中。在第二次请求时,它将对本地文件进行检查,如果不能确定这份本地文件是否可以直接使用,它将会发起一次条件请求。所谓条件请求,就是在普通的 GET 请求报文中,附带 If- Modified-Since 字段,如下所示:
If- Modified-Since
If-Modified-Since: Sun, 03 Feb 2013 06:01:12 GMT
它将询问服务器端是否有更新的版本,本地文件的最后修改时间。如果服务器端没有新的版本,只需响应一个304状态码,客户端就使用本地版本。如果服务器端有新的版本,就将新的内容发送给客户端,客户端放弃本地版本。代码如下所示:
var handle = function (req, res) { fs.stat(filename, function (err, stat) { var lastModified = stat.mtime.toUTCString(); if (lastModified === req.headers['if-modified-since']) { res.writeHead(304, "Not Modified"); res.end(); } else { fs.readFile(filename, function (err, file) { var lastModified = stat.mtime.toUTCString(); res.setHeader("Last-Modified", lastModified); res.writeHead(200, "Ok"); res.end(file); }); } }); }
这里的条件请求采用时间戳的方式实现,但是时间戳有一些缺陷存在。
为此HTTP1.1中引入了ETag来解决这个问题。ETag 的全称是Entity Tag,由服务器端生成,服务器端可以决定它的生成规则。如果根据文件内容生成散列值,那么条件请求将不会受到时间戳改动造成的带宽浪费。下面是根据内容生成散列值的方法:
ETag
var getHash = function (str) { var shasum = crypto.createHash('sha1'); return shasum.update(str).digest('base64'); };
与 If-Modified-Since/Last-Modified 不同的是,ETag的请求和响应是 If-None-Match/ETag ,如下所示:
If-Modified-Since/Last-Modified
If-None-Match/ETag
var handle = function (req, res) { fs.readFile(filename, function (err, file) { var hash = getHash(file); var noneMatch = req.headers['if-none-match']; if (hash === noneMatch) { res.writeHead(304, "Not Modified"); res.end(); } else { res.setHeader("ETag", hash); res.writeHead(200, "Ok"); res.end(file); } }); };
浏览器在收到 ETag: "83-1359871272000" 这样的响应后,在下次的请求中,会将其放置在请求头中: If-None-Match: "83-1359871272000" 。
ETag: "83-1359871272000"
If-None-Match: "83-1359871272000"
尽管条件请求可以在文件内容没有修改的情况下节省带宽,但是它依然会发起一个HTTP请求,使得客户端依然会花一定时间来等待响应。可见最好的方案就是连条件请求都不用发起。那么如何让浏览器知晓是否能直接使用本地版本呢?答案就是服务器端在响应内容时,让浏览器明确地将内容缓存起来。在响应里设置 Expires 或 Cache-Control 头,浏览器将根据该值进行缓存。那么这两个值有何区别呢?
Expires
Cache-Control
HTTP1.0时,在服务器端设置 Expires 可以告知浏览器要缓存文件内容,如下代码所示:
var handle = function (req, res) { fs.readFile(filename, function (err, file) { var expires = new Date(); expires.setTime(expires.getTime() + 10 * 365 * 24 * 60 * 60 * 1000); res.setHeader("Expires", expires.toUTCString()); res.writeHead(200, "Ok"); res.end(file); }); };
Expires 是一个GMT格式的时间字符串。浏览器在接到这个过期值后,只要本地还存在这个缓存文件,在到期时间之前它都不会再发起请求。
但是 Expires 的缺陷在于浏览器与服务器之间的时间可能不一致,这可能会带来一些问题,比如文件提前过期,或者到期后并没有被删除。在这种情况下, Cache-Control 以更丰富的形式,实现相同的功能,如下所示:
var handle = function (req, res) { fs.readFile(filename, function (err, file) { res.setHeader("Cache-Control", "max-age=" + 10 * 365 * 24 * 60 * 60 * 1000); res.writeHead(200, "Ok"); res.end(file); }); };
上面的代码为 Cache-Control 设置了 max-age 值,它比 Expires 优秀的地方在于,Cache-Control 能够避免浏览器端与服务器端时间不同步带来的不一致性问题,只要进行类似倒计时的方式计算过期时间即可。除此之外, Cache-Control 的值还能设置 public、 private 、 no-cache 、 no-store 等能够更精细地控制缓存的选项。
public
private
no-cache
no-store
由于在HTTP1.0时还不支持 max-age ,如今的服务器端在模块的支持下多半同时对 Expires 和 Cache-Control 进行支持。在浏览器中如果两个值同时存在,且被同时支持时, max-age 会覆盖 Expires 。
max-age
The text was updated successfully, but these errors were encountered:
No branches or pull requests
使用缓存的流程图
![image](https://user-images.githubusercontent.com/26681854/55385672-3bcca700-5560-11e9-85bf-69e73e17dc30.png)
简单来讲,本地没有文件时,浏览器必然会请求服务器端的内容,并将这部分内容放置在本地的某个缓存目录中。在第二次请求时,它将对本地文件进行检查,如果不能确定这份本地文件是否可以直接使用,它将会发起一次条件请求。所谓条件请求,就是在普通的 GET 请求报文中,附带
If- Modified-Since
字段,如下所示:它将询问服务器端是否有更新的版本,本地文件的最后修改时间。如果服务器端没有新的版本,只需响应一个304状态码,客户端就使用本地版本。如果服务器端有新的版本,就将新的内容发送给客户端,客户端放弃本地版本。代码如下所示:
这里的条件请求采用时间戳的方式实现,但是时间戳有一些缺陷存在。
为此HTTP1.1中引入了
ETag
来解决这个问题。ETag
的全称是Entity Tag,由服务器端生成,服务器端可以决定它的生成规则。如果根据文件内容生成散列值,那么条件请求将不会受到时间戳改动造成的带宽浪费。下面是根据内容生成散列值的方法:与
If-Modified-Since/Last-Modified
不同的是,ETag的请求和响应是If-None-Match/ETag
,如下所示:浏览器在收到
ETag: "83-1359871272000"
这样的响应后,在下次的请求中,会将其放置在请求头中:If-None-Match: "83-1359871272000"
。尽管条件请求可以在文件内容没有修改的情况下节省带宽,但是它依然会发起一个HTTP请求,使得客户端依然会花一定时间来等待响应。可见最好的方案就是连条件请求都不用发起。那么如何让浏览器知晓是否能直接使用本地版本呢?答案就是服务器端在响应内容时,让浏览器明确地将内容缓存起来。在响应里设置
Expires
或Cache-Control
头,浏览器将根据该值进行缓存。那么这两个值有何区别呢?HTTP1.0时,在服务器端设置
Expires
可以告知浏览器要缓存文件内容,如下代码所示:Expires
是一个GMT格式的时间字符串。浏览器在接到这个过期值后,只要本地还存在这个缓存文件,在到期时间之前它都不会再发起请求。但是
Expires
的缺陷在于浏览器与服务器之间的时间可能不一致,这可能会带来一些问题,比如文件提前过期,或者到期后并没有被删除。在这种情况下,Cache-Control
以更丰富的形式,实现相同的功能,如下所示:上面的代码为
Cache-Control
设置了 max-age 值,它比Expires
优秀的地方在于,Cache-Control
能够避免浏览器端与服务器端时间不同步带来的不一致性问题,只要进行类似倒计时的方式计算过期时间即可。除此之外,Cache-Control
的值还能设置public
、private
、no-cache
、no-store
等能够更精细地控制缓存的选项。由于在HTTP1.0时还不支持
max-age
,如今的服务器端在模块的支持下多半同时对Expires
和Cache-Control
进行支持。在浏览器中如果两个值同时存在,且被同时支持时,max-age
会覆盖Expires
。The text was updated successfully, but these errors were encountered: