node.js实现http服务器与浏览器之间的内容缓存操作示例


Posted in Javascript onFebruary 11, 2020

本文实例讲述了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程序设计有所帮助。

Javascript 相关文章推荐
jQuery实战之仿淘宝商城左侧导航效果
Apr 12 Javascript
Javascript实现计算个人所得税
May 10 Javascript
js实现点击按钮弹出上传文件的窗口
Dec 23 Javascript
使用vue.js实现checkbox的全选和多个的删除功能
Feb 17 Javascript
基于vue中对鼠标划过事件的处理方式详解
Aug 22 Javascript
详解vue项目中实现图片裁剪功能
Jun 07 Javascript
vue使用showdown并实现代码区域高亮的示例代码
Oct 17 Javascript
jquery实现掷骰子小游戏
Oct 24 jQuery
Vue Cli3 打包配置并自动忽略console.log语句的方法
Apr 23 Javascript
Vue解决移动端弹窗滚动穿透问题
Dec 15 Vue.js
AudioContext 实现音频可视化(web技术分享)
Feb 24 Javascript
浅谈Vue的computed计算属性
Mar 21 Vue.js
node.js 使用 net 模块模拟 websocket 握手进行数据传递操作示例
Feb 11 #Javascript
JavaScript实现拖拽功能
Feb 11 #Javascript
node.js中 mysql 增删改查操作及async,await处理实例分析
Feb 11 #Javascript
基于Angular 8和Bootstrap 4实现动态主题切换的示例代码
Feb 11 #Javascript
原生js实现点击轮播切换图片
Feb 11 #Javascript
node.js中process进程的概念和child_process子进程模块的使用方法示例
Feb 11 #Javascript
小程序如何定位所在城市及发起周边搜索
Feb 11 #Javascript
You might like
PHP实现的简单留言板功能示例【基于thinkPHP框架】
2018/12/07 PHP
php连接sftp的作用以及实例代码
2019/09/23 PHP
Yii框架多语言站点配置方法分析【中文/英文切换站点】
2020/04/07 PHP
javascript通过class来获取元素实现代码
2013/02/20 Javascript
js时间日期和毫秒的相互转换
2013/02/22 Javascript
Extjs4 关于Store的一些操作(加载/回调/添加)
2013/04/18 Javascript
jQuery之过滤元素操作小结
2013/11/30 Javascript
Jquery 过滤器(first,last,not,even,odd)的使用
2014/01/22 Javascript
js style动态设置table高度
2014/10/21 Javascript
js实现界面向原生界面发消息并跳转功能
2016/11/22 Javascript
在JS中a标签加入单击事件屏蔽href跳转页面
2016/12/16 Javascript
Bootstrap fileinput组件封装及使用详解
2017/03/10 Javascript
Angular.JS中指令ng-if、ng-show/ng-hide和ng-switch的使用教程
2017/05/07 Javascript
利用jquery如何从json中读取数据追加到html中
2017/12/01 jQuery
vue.js input框之间赋值方法
2018/08/24 Javascript
解决Angular4项目部署到服务器上刷新404的问题
2018/08/31 Javascript
微信小程序实现两个页面传值的方法分析
2018/12/11 Javascript
vue视频播放暂停代码
2019/11/08 Javascript
JS实现TITLE悬停长久显示效果完整示例
2020/02/11 Javascript
[01:34]2016国际邀请赛中国区预选赛IG战队教练采访
2016/06/27 DOTA
在Django框架中伪造捕捉到的URLconf值的方法
2015/07/18 Python
Python基于回溯法子集树模板实现图的遍历功能示例
2017/09/05 Python
Python内建函数之raw_input()与input()代码解析
2017/10/26 Python
使用Python制作微信跳一跳辅助
2018/01/31 Python
详解Python装饰器
2019/03/25 Python
python实现的批量分析xml标签中各个类别个数功能示例
2019/12/30 Python
基于梯度爆炸的解决方法:clip gradient
2020/02/04 Python
英国乐购杂货:Tesco Groceries
2018/11/29 全球购物
职业道德模范事迹材料
2014/08/24 职场文书
领导班子在批评与自我批评座谈会上的发言
2014/09/28 职场文书
培训计划通知
2015/07/15 职场文书
2016年学校综治宣传月活动总结
2016/03/16 职场文书
2017年大学生寒假社会实践活动总结
2016/04/06 职场文书
60句有关成长的名言
2019/09/04 职场文书
Mysql忘记密码解决方法
2022/02/12 MySQL
索尼ICF-36收音机评测
2022/04/30 无线电