本文实例讲述了node.js实现http服务器与浏览器之间的内容缓存操作。分享给大家供大家参考,具体如下:
一、缓存的作用
1、减少了数据传输,节约流量。
2、减少服务器压力,提高服务器性能。
3、加快客户端加载页面的速度。
二、缓存的分类
1、强制缓存,如果缓存有效,则不需要与服务器发生交互,直接使用缓存。
2、对比缓存,每次都需要与服务器发生交互,对缓存进行比较判断是否可以使用缓存。
三、通过使用 Last-Modified / If-Modified-Since 来进行缓存判断
1、Last-Modified 是服务器向客户端发送的头信息,用于告诉客户端资源的 最后修改时间,该信息浏览器会保存起来。
2、If-Modified-Since 是客户端向服务器发送的头信息,当客户端再次请求资源时,浏览器会带上该信息发送给服务器,服务器通过该信息来判断资源是否过期。
3、如果没有过期,则响应 304 表示 未更新,告诉浏览器使用保存的缓存。
4、如果过期了,则响应 200,返回最新的资源。
const http = require('http'); const url = require('url'); const path = require('path'); const fs = require('fs'); const util = require('util'); const mime = require('mime'); //创建http服务器并监听端口 let server = http.createServer(); server.listen(1234, '0.0.0.0', function () { console.log('开始监听'); }); function sendFile(req, res, filePath, stats) { //设置文件内容类型 res.setHeader('Content-Type', mime.getType(filePath)); //设置资源最后修改时间头信息 res.setHeader('Last-Modified', stats.ctime.toGMTString()); //通过管道将文件数据发送给客户端 fs.createReadStream(filePath).pipe(res); } server.on('request', function (req, res) { let {pathname} = url.parse(req.url, true); //获取文件真实路径 let filePath = path.join(__dirname, pathname); //判断文件是否存在 fs.stat(filePath, function (err, stats) { if (err) { return res.end(util.inspect(err)); } if (!stats.isFile()) { return res.end('is not file'); } //获取客户端请求的If-Modified-Since头信息 let ifModifiedSince = req.headers['if-modified-since']; if (ifModifiedSince) { //如果最后修改时间相同,说明该资源并未修改,直接响应 304,让浏览器从缓存中获取数据。 if (ifModifiedSince == stats.ctime.toGMTString()) { res.statusCode = 304; res.end(); } else { sendFile(req, res, filePath, stats); } } else { sendFile(req, res, filePath, stats); } }); });
通过最后修改时间判断缓存是否可用,并不是很精确,有如下几个问题:
1、Last-Modified 只精确到秒,秒以下的时间修改,将无法准确判断。
2、文件最后修改时间变了,但 内容并没有发生改变。
3、文件存在于多个 CDN 上,那该文件的最后修改时间是不一样的。
四、通过 ETag / If-None-Match 进行判断
ETag 表示 实体标签,将内容通过 hash 算法生成一段字符串,用以标识资源,如果资源发生变化,则 ETag 也会变化。
ETag 是服务器生成的,发送给客户端的。
1、客户端请求资源,服务器根据资源生成ETag,发送给客户端。浏览器会保存该信息。
2、当客户端再次请求时,浏览器会发送 If-None-Match 给服务器,值为第1步保存的信息,服务器通过该信息进行判断,资源是否修改过。
3、如果没有修改过,则响应 304 未更新,告诉浏览器使用保存的缓存。
4、如果修改过,则响应 200,返回最新资源。
const http = require('http'); const url = require('url'); const path = require('path'); const fs = require('fs'); const util = require('util'); const crypto = require('crypto'); const mime = require('mime'); //创建http服务器并监听端口 let server = http.createServer(); server.listen(1234, '0.0.0.0', function () { console.log('开始监听'); }); function sendFile(req, res, filePath, eTag) { //设置文件内容类型 res.setHeader('Content-Type', mime.getType(filePath)); //设置ETag头信息 res.setHeader('ETag', eTag); //通过管道将文件数据发送给客户端 fs.createReadStream(filePath).pipe(res); } server.on('request', function (req, res) { let {pathname} = url.parse(req.url, true); //获取文件真实路径 let filePath = path.join(__dirname, pathname); //判断文件是否存在 fs.stat(filePath, function (err, stats) { if (err) { return res.end(util.inspect(err)); } if (!stats.isFile()) { return res.end('is not file'); } //获取客户端请求的If-None-Match头信息 let ifNoneMatch = req.headers['if-none-match']; //创建可读流 let rs = fs.createReadStream(filePath); //创建md5算法 let md5 = crypto.createHash('md5'); rs.on('data', function (data) { md5.update(data); }); rs.on('end', function () { let eTag = md5.digest('hex'); if (ifNoneMatch) { //判断eTag与客户端发送过来的If-None-Match是否相等 if (ifNoneMatch == eTag) { res.statusCode = 304; res.end(); } else { sendFile(req, res, filePath, eTag); } } else { sendFile(req, res, filePath, eTag); } }); }); });
五、让浏览器在缓存有效期内不用发请求
Expires 是http1.0的内容,用于设置缓存的有效期,在有效期内浏览器直接从浏览器缓存中获取数据。
Cache-Control 与Expires作用一样,是http1.1的内容,用于指明当前资源的有效期,优先级高于Expires。
Cache-Control可以设置的值 :
1、private 客户端可以缓存
2、public 客户端和代理服务器都可以缓存
3、max-age=10 缓存内容在10秒后失效
4、no-cache 使用对比缓存验证,强制向服务器验证
5、no-store 内容都不缓存,强制缓存和对比缓存都不会触发
const http = require('http'); const url = require('url'); const path = require('path'); const fs = require('fs'); const util = require('util'); const mime = require('mime'); //创建http服务器并监听端口 let server = http.createServer(); server.listen(1234, '0.0.0.0', function () { console.log('开始监听'); }); function sendFile(req, res, filePath, stats) { //设置文件内容类型 res.setHeader('Content-Type', mime.getType(filePath)); //设置缓存失效时间60秒 res.setHeader('Expires', new Date(Date.now() + 60 * 1000).toUTCString()); //设置缓存失效时间60秒 res.setHeader('Cache-Control', 'max-age=60'); //通过管道将文件数据发送给客户端 fs.createReadStream(filePath).pipe(res); } server.on('request', function (req, res) { let {pathname} = url.parse(req.url, true); //获取文件真实路径 let filePath = path.join(__dirname, pathname); //判断文件是否存在 fs.stat(filePath, function (err, stats) { if (err) { return res.end(util.inspect(err)); } if (!stats.isFile()) { return res.end('is not file'); } sendFile(req, res, filePath, stats) }); });
希望本文所述对大家node.js程序设计有所帮助。
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 【雨果唱片】中国管弦乐《鹿回头》WAV
- APM亚流新世代《一起冒险》[FLAC/分轨][106.77MB]
- 崔健《飞狗》律冻文化[WAV+CUE][1.1G]
- 罗志祥《舞状元 (Explicit)》[320K/MP3][66.77MB]
- 尤雅.1997-幽雅精粹2CD【南方】【WAV+CUE】
- 张惠妹.2007-STAR(引进版)【EMI百代】【WAV+CUE】
- 群星.2008-LOVE情歌集VOL.8【正东】【WAV+CUE】
- 罗志祥《舞状元 (Explicit)》[FLAC/分轨][360.76MB]
- Tank《我不伟大,至少我能改变我。》[320K/MP3][160.41MB]
- Tank《我不伟大,至少我能改变我。》[FLAC/分轨][236.89MB]
- CD圣经推荐-夏韶声《谙2》SACD-ISO
- 钟镇涛-《百分百钟镇涛》首批限量版SACD-ISO
- 群星《继续微笑致敬许冠杰》[低速原抓WAV+CUE]
- 潘秀琼.2003-国语难忘金曲珍藏集【皇星全音】【WAV+CUE】
- 林东松.1997-2039玫瑰事件【宝丽金】【WAV+CUE】