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简单事件处理和with用法介绍
Sep 16 Javascript
JS短路原理的应用示例 精简代码的途径
Dec 13 Javascript
jquery的each方法使用示例分享
Mar 25 Javascript
jQuery通过点击行来删除HTML表格行的实现示例
Sep 10 Javascript
js实现的tab标签切换效果代码分享
Aug 25 Javascript
在JavaScript中对HTML进行反转义详解
May 18 Javascript
jquery输入数字随机抽奖特效的简单实现代码
Jun 10 Javascript
浅谈JS对html标签的属性的干预以及对CSS样式表属性的干预
Jun 25 Javascript
vue.js  父向子组件传参的实例代码
Oct 29 Javascript
JS实现图片旋转动画效果封装与使用示例
Jul 09 Javascript
JQuery常见节点操作实例分析
May 15 jQuery
微信小程序利用云函数获取手机号码
Dec 17 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
PHPlet在Windows下的安装
2006/10/09 PHP
Joomla下利用configuration.php存储简单数据
2010/05/19 PHP
php 搜索框提示(自动完成)实例代码
2012/02/05 PHP
PHP与MYSQL中UTF8编码的中文排序实例
2014/10/21 PHP
Yii2实现同时搜索多个字段的方法
2016/08/10 PHP
php实现有序数组打印或排序的方法【附Python、C及Go语言实现代码】
2016/11/10 PHP
jQuery 瀑布流 浮动布局(一)(延迟AJAX加载图片)
2012/05/23 Javascript
js运动框架_包括图片的淡入淡出效果
2013/05/11 Javascript
jQuery判断元素是否存在的可靠方法
2014/05/06 Javascript
学习JavaScript设计模式之代理模式
2016/01/12 Javascript
AngularJS 指令详细介绍
2016/07/27 Javascript
Js得到radiobuttonlist选中值的两种方法(推荐)
2016/08/25 Javascript
js与jquery分别实现tab标签页功能的方法
2016/11/18 Javascript
巧用数组制作图片切换js代码
2016/11/29 Javascript
Bootstrap table学习笔记(2) 前后端分页模糊查询
2017/05/18 Javascript
强大的JavaScript响应式图表Chartist.js的使用
2017/09/13 Javascript
浅谈Vue SPA 首屏加载优化实践
2017/12/15 Javascript
AngularJS双向数据绑定原理之$watch、$apply和$digest的应用
2018/01/30 Javascript
js 数组详细操作方法及解析合集
2018/06/01 Javascript
vue.js实现带日期星期的数字时钟功能示例
2018/08/28 Javascript
Element-ui中元素滚动时el-option超出元素区域的问题
2019/05/30 Javascript
layui插件表单验证提交触发提交的例子
2019/09/09 Javascript
Python通过websocket与js客户端通信示例分析
2014/06/25 Python
python实现简单socket程序在两台电脑之间传输消息的方法
2015/03/13 Python
Python对字符串实现去重操作的方法示例
2017/08/11 Python
Python实现利用最大公约数求三个正整数的最小公倍数示例
2017/09/30 Python
python批量获取html内body内容的实例
2019/01/02 Python
对python3中的RE(正则表达式)-详细总结
2019/07/23 Python
Python处理session的方法整理
2019/08/29 Python
PyTorch实现更新部分网络,其他不更新
2019/12/31 Python
如何实现一个python函数装饰器(Decorator)
2020/10/12 Python
python3中calendar返回某一时间点实例讲解
2020/11/18 Python
使用postMessage让 iframe自适应高度的方法示例
2019/10/08 HTML / CSS
加拿大花店:1800Flowers.ca
2016/11/16 全球购物
农村产权制度改革实施方案
2014/03/21 职场文书
python读取mat文件生成h5文件的实现
2022/07/15 Python