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 相关文章推荐
$()JS小技巧
Jul 21 Javascript
JavaScript基础知识之数据类型
Aug 06 Javascript
js修改table中Td的值(定义td的双击事件)
Jan 10 Javascript
jquery插件jquery倒计时插件分享
Dec 27 Javascript
单击某一段文字改写文本颜色
Jun 06 Javascript
jQuery之DOM对象和jQuery对象的转换与区别分析
Jan 08 Javascript
react native带索引的城市列表组件的实例代码
Aug 08 Javascript
浅谈vue-router2路由参数注意的问题
Nov 08 Javascript
vue里面v-bind和Props 利用props绑定动态数据的方法
Aug 27 Javascript
element-ui多文件上传的实现示例
Apr 10 Javascript
微信小程序 调用远程接口 给全局数组赋值代码实例
Aug 13 Javascript
前端vue-cli项目中使用img图片和background背景图的几种方法
Nov 13 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下连接mssql2005的代码
2011/01/17 PHP
php学习笔记之 函数声明
2011/06/09 PHP
PHP 无限分类三种方式 非函数的递归调用!
2011/08/26 PHP
php使用date和strtotime函数输出指定日期的方法
2014/11/14 PHP
php返回相对时间(如:20分钟前,3天前)的方法
2015/04/14 PHP
如何通过Linux命令行使用和运行PHP脚本
2015/07/29 PHP
javascript脚本编程解决考试分数统计问题
2008/10/18 Javascript
在Javascript中 声明时用"var"与不用"var"的区别
2013/04/15 Javascript
利用javascript实现web页面中指定区域打印
2013/10/30 Javascript
javascript确认框的三种使用方法
2013/12/17 Javascript
JavaScript创建对象的方式小结(4种方式)
2015/12/17 Javascript
Angular.js实现动态加载组件详解
2017/05/28 Javascript
详解基于Vue cli生成的Vue项目的webpack4升级
2018/06/19 Javascript
详解Axios 如何取消已发送的请求
2018/10/20 Javascript
vant(ZanUi)结合async-validator实现表单验证的方法
2018/12/06 Javascript
微信小程序使用Vant Weapp组件库的方法步骤
2019/08/01 Javascript
layui输入框中只允许输入整数的实现方法
2019/09/18 Javascript
微信小程序实现拨打电话功能的示例代码
2020/06/28 Javascript
Linux下为不同版本python安装第三方库
2016/08/31 Python
Python中index()和seek()的用法(详解)
2017/04/27 Python
详解Python list 与 NumPy.ndarry 切片之间的对比
2017/07/24 Python
Python多进程multiprocessing.Pool类详解
2018/04/27 Python
python一键去抖音视频水印工具
2018/09/14 Python
Django时区详解
2019/07/24 Python
利用Python优雅的登录校园网
2020/10/21 Python
ProBikeKit德国:在线公路自行车专家
2018/06/03 全球购物
男女钓鱼靴和甲板鞋:XTRATUF
2021/01/09 全球购物
介绍一下Linux内核的排队自旋锁
2014/08/27 面试题
迟到早退检讨书
2014/02/10 职场文书
五星级酒店餐饮部总监的标准岗位职责
2014/02/17 职场文书
以幸福为主题的活动方案
2014/08/22 职场文书
2015年信访维稳工作总结
2015/04/07 职场文书
保护校园环境倡议书
2015/04/28 职场文书
惊天动地观后感
2015/06/10 职场文书
Python基本的内置数据类型及使用方法
2022/04/13 Python
python高温预警数据获取实例
2022/07/23 Python