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实现翻动fadeIn显示的方法
Mar 05 Javascript
JavaScript学习笔记之检测客户端类型是(引擎、浏览器、平台、操作系统、移动设备)
Dec 03 Javascript
Avalon中文长字符截取、关键字符隐藏、自定义过滤器
May 18 Javascript
jqueryMobile 动态添加元素,展示刷新视图的实现方法
May 28 Javascript
JS简单生成两个数字之间随机数的方法
Aug 03 Javascript
jquery实现拖动效果
Aug 10 Javascript
jQuery ajax的功能实现方法详解
Jan 06 Javascript
webpack踩坑之路图片的路径与打包
Sep 05 Javascript
基于jquery的on和click的区别详解
Jan 15 jQuery
JavaScript 截取字符串代码实例
Sep 05 Javascript
将Vue组件库更换为按需加载的方法步骤
May 06 Javascript
JavaScript forEach中return失效问题解决方案
Jun 01 Javascript
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验证码类代码( 最新修改,完全定制化! )
2010/12/02 PHP
php获取根域名方法汇总
2014/10/28 PHP
PHP统计数值数组中出现频率最多的10个数字的方法
2015/04/20 PHP
php将文件夹打包成zip文件的简单实现方法
2016/10/04 PHP
Yii2中简单的场景使用介绍
2017/06/02 PHP
php处理多图上传压缩代码功能
2018/06/13 PHP
thinkphp5.1框架实现格式化mysql时间戳为日期的方式小结
2019/10/10 PHP
Thinkphp5.0 框架Model模型简单用法分析
2019/10/11 PHP
在javascript中关于节点内容加强
2013/04/11 Javascript
JS保留两位小数,多位小数的示例代码
2014/01/07 Javascript
禁止页面刷新让F5快捷键及右键都无效
2014/01/22 Javascript
javascript中数组的定义及使用实例
2015/01/21 Javascript
javascript 中的 delete及delete运算符
2015/11/15 Javascript
非常实用的js验证框架实现源码 附原理方法
2016/06/08 Javascript
又一枚精彩的弹幕效果jQuery实现
2016/07/25 Javascript
AngularJs bootstrap详解及示例代码
2016/09/01 Javascript
javascript 的变量、作用域和内存问题
2017/04/19 Javascript
nodejs入门教程三:调用内部和外部方法示例
2017/04/24 NodeJs
Angular中ng-repeat与ul li的多层嵌套重复问题
2017/07/24 Javascript
简单明了区分escape、encodeURI和encodeURIComponent
2018/05/26 Javascript
[01:20]DOTA2 齐天大圣至宝动态展示
2016/12/13 DOTA
[01:45:05]VGJ.T vs Newbee Supermajor 败者组 BO3 第二场 6.6
2018/06/07 DOTA
Python的Flask框架中Flask-Admin库的简单入门指引
2015/04/07 Python
Python操作SQLite数据库的方法详解【导入,创建,游标,增删改查等】
2017/07/11 Python
利用python操作SQLite数据库及文件操作详解
2017/09/22 Python
python矩阵转换为一维数组的实例
2018/06/05 Python
浅析Python中字符串的intern机制
2020/10/03 Python
你可能不熟练的十个前端HTML5经典面试题
2018/07/03 HTML / CSS
联想新加坡官方网站:Lenovo Singapore
2017/10/24 全球购物
土木工程个人自荐信范文
2013/11/30 职场文书
优秀学生自我鉴定范例
2013/12/18 职场文书
《油菜花开了》教学反思
2014/02/22 职场文书
三项教育活动实施方案
2014/03/30 职场文书
授权委托书格式模板
2014/04/03 职场文书
宾馆仓管员岗位职责
2014/07/27 职场文书
材料员岗位职责范本
2015/04/11 职场文书