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 相关文章推荐
浅谈Javascript事件处理程序的几种方式
Jun 27 Javascript
jquery实现简单的拖拽效果实例兼容所有主流浏览器
Jun 21 Javascript
js中哈希表的几种用法总结
Jan 28 Javascript
原生javascript实现的分页插件pagenav
Aug 28 Javascript
JS中的Replace方法使用经验分享
May 20 Javascript
Javascript中内建函数reduce的应用详解
Oct 20 Javascript
javascript 网页进度条简单实例
Feb 22 Javascript
JS实现的模仿QQ头像资料卡显示与隐藏效果
Apr 07 Javascript
详解开源的JavaScript插件化框架MinimaJS
Oct 26 Javascript
jquery拖拽自动排序插件使用方法详解
Jul 20 jQuery
React Native中Mobx的使用方法详解
Dec 04 Javascript
详细教你微信公众号正文页SVG交互开发技巧
Jul 25 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 数组入门教程小结
2009/05/20 PHP
PHP字符串的编码问题的详细介绍
2013/04/27 PHP
Laravel 5.3 学习笔记之 安装
2016/08/28 PHP
php的常量和变量实例详解
2017/06/27 PHP
Javascript学习笔记1 数据类型
2010/01/11 Javascript
jQuery图片预加载 等比缩放实现代码
2011/10/04 Javascript
js判断生效时间不得大于失效时间的思路及代码
2013/04/23 Javascript
禁止页面刷新让F5快捷键及右键都无效
2014/01/22 Javascript
纯js写的分页表格数据为json串
2014/02/18 Javascript
使用jQuery中的when实现多个AJAX请求对应单个回调的例子分享
2014/04/23 Javascript
bootstrap modal弹出框的垂直居中
2016/12/14 Javascript
微信小程序之多文件下载的简单封装示例
2018/01/29 Javascript
vuejs实现标签选项卡动态更改css样式的方法
2018/05/31 Javascript
jQuery实现表单动态添加数据并提交的方法
2018/07/19 jQuery
小程序云函数调用API接口的方法
2019/05/17 Javascript
解决vue admin element noCache设置无效的问题
2019/11/12 Javascript
详解vuejs中执行npm run dev出现页面cannot GET/问题
2020/04/26 Javascript
在vue项目中利用popstate处理页面返回的操作介绍
2020/08/06 Javascript
python之pyqt5通过按钮改变Label的背景颜色方法
2019/06/13 Python
bluepy 一款python封装的BLE利器简单介绍
2019/06/25 Python
解决pytorch GPU 计算过程中出现内存耗尽的问题
2019/08/19 Python
python的移位操作实现详解
2019/08/21 Python
利用pandas向一个csv文件追加写入数据的实现示例
2020/04/23 Python
selenium框架中driver.close()和driver.quit()关闭浏览器
2020/12/08 Python
海淘母婴商城:国际妈咪
2016/07/23 全球购物
国贸专业个人求职信范文
2014/01/08 职场文书
班主任工作经验材料
2014/02/02 职场文书
《小鹰学飞》教学反思
2014/04/23 职场文书
幼儿园安全工作总结2015
2015/04/20 职场文书
社区法制宣传日活动总结
2015/05/05 职场文书
青涩记忆观后感
2015/06/18 职场文书
校运会班级霸气口号
2015/12/24 职场文书
Django 如何实现文件上传下载
2021/04/08 Python
Python 实现定积分与二重定积分的操作
2021/05/26 Python
MySQL索引是啥?不懂就问
2021/07/21 MySQL
Python开发五子棋小游戏
2022/04/28 Python