当前位置 博文首页 > 如何利用node实现静态文件缓存详解

    如何利用node实现静态文件缓存详解

    作者:nameIsZoe27 时间:2021-09-06 19:06

    目录
    • 缓存
      • 缓存位置分类
      • 缓存设置header
    • node实现静态文件缓存
      • 强缓存
        • 思路
        • 代码实现
        • 效果展示
      • 协商缓存
        • 思路
        • 代码实现
        • 效果展示
    • 总结

      缓存

      浏览器缓存(Brower Caching)是浏览器对之前请求过的文件进行缓存,以便下一次访问时重复使用,节省带宽,提高访问速度,降低服务器压力

      缓存位置分类

      memory cache:内存中的缓存,关闭浏览器则清空,一般存储一些js库

      disk cache:硬盘中的缓存,关闭浏览器不会马上清空,一般存储大文件,比如 图片资源,iconFont这类的图标文件库

      两者的区别:

        1. 读取速度 :memory cache缓存的是当前解析过了的文件在浏览器tab进程里,下次运行使用时的可以快速读取;

                    disk cache直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O(读取)操作,然后重新解析缓存内容,速度比内存缓存慢

        2. 时效性:memory cache是存在tab的进程里,tab关闭,则清空;

                  disk cache:被清空的时机我还不知道(希望有人可以补充)

        3. 优先级:memory cache大于disk cache

        对于大文件来说,大概率是不存储在memory中的,反之优先,代码角度目前好像也无法控制浏览器缓存位置

      缓存设置header

      cache-control

       1.   cache-control:max-age=10//10秒内重新发的请求都直接命中强缓存,无需向服务器发起请求,读取浏览器缓存即可
       2.   Cache-Control:no-cache //禁止强制缓存,每次都向服务器发起请求,同时也会存在浏览器缓存中 (走协商缓存了基本)
       3.   Cache-Control:no-store //每次都请求服务器,且不缓存在浏览器中,等同于没有缓存 
      复制代码

      Expires:

      兼容低版本浏览器,这个就是设置绝对时间,获取的是服务器的当前时间和浏览器当前时间做比对(通常存在偏差,是http1.0的产物),和 cache-control同时存在时,cache-control优先级更高

      • last-modified:协商缓存的时候用 和If-Modified-Since,成对出现;If-Modified-Since请求头的值对应上一次服务器的响应头last-modified的值,拥有提供服务器比对请求资源修改时间,相等,则命中协商缓存返回304,浏览器读取缓存即可
      • Etag:资源标识(也有说时指纹,通常是一个md5值),协商缓存时候用,比较文件是否修改;和If-None-Match 成对出现

      Etag主要为了解决 Last-Modified 无法解决的一些问题。

      1. 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;

      2. 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since无法检查到如此精细

      3. 某些服务器不能精确的得到文件的最后修改时间;

      4.Etag与Last-modify同时存在 Etag优先级比较

      实际项目:html不允许缓存,html里引用的js有唯一的版本号做依据,再次访问的时候 访问最新的html,引用的js或其他文件版本号未修改则直接用本地缓存

      node实现静态文件缓存

      文件结构

      public对应我们测试用的静态资源

      强缓存

      思路

      • 创建服务
      • 首次请求 解析请求路径, fs.createReadStream().pipe() 读取文件
      • 设置响应头Cache-Contro:max-age=10 强缓存的相对时间

      代码实现

      const http = require("http");
      const url = require("url");
      const fs = require("fs");
      const path = require("path");
      // 接收文件路径 返回该文件对应的文件类型格式
      const mime = require("mime");//npm i mime 
      
      const server = http.createServer((req, res) => {
        let { pathname, query } = url.parse(req.url, true);
        //__dirname 当前文件所在的文件夹所处的绝对路径 和请求路径拼接
        let filePath = path.join(__dirname, "public", pathname);
        console.log(req.url);//10s内反复刷新页面,查看是否持续打印,命中强缓存则10s打印一次
        // 设置头部 缓存信息,规定的缓存时间内,客户端无需再向服务器发起请求
        res.setHeader("Cache-Control", "max-age=10"); // 设置缓存时常;请求的当前时间+max-age 的相对时间内,优先级比Expires高
        res.setHeader("Expires", new Date(Date.now() + 10).toUTCString()); //兼容低版本浏览器,这个就是设置绝对时间,获取的是服务器的当前时间
        // 获取请求路径 判断是文件还是文件目录
        fs.stat(filePath, function (err, statObj) {
          // url解析错误,则请求错误 没有找到对应url资源 返回404
          if (err) {
            res.statusCode = 404;
            res.end("NOT FOUND");
          } else {
            // 如果是文件,用可读流+管道 pipe 进行文件内容读取,利用mime 获取文件内容格式,并设置编码规范为utf-8
            if (statObj.isFile()) {
              fs.createReadStream(filePath).pipe(res);
              res.setHeader(
                "Content-Type",
                mime.getType(filePath) + ";charset=utf-8"
              );
            } else {
              // 如果是文件目录 找到 目录下对应的index.html
              let htmlPath = path.join(filePath, "index.html");
              // fs.access判断拼接的路径是否可访问
              fs.access(htmlPath, function (err) {
                if (err) {
                  // 不可访问 设置 状态码404
                  res.statusCode = 404;
                  res.end("NOT FOUND");
                } else {
                  //可访问,用可读流加管道 pipe 进行文件内容读取
                  fs.createReadStream(htmlPath).pipe(res);
                  res.setHeader("Content-Type", "text/html;charset=utf-8");
                }
              });
            }
          }
        });
        // 写到这里 可以 nodemon cache.js  启动服务 查看 http://localhost:3000/ 
      });
      server.listen(3000, () => {
        console.log("server start 3000");
      });
      
      

      效果展示

      协商缓存

      成功

      思路

      • 创建服务
      • 首次请求 解析请求路径, fs.createReadStream().pipe() 读取文件
      • 设置响应头Last-modified 返回浏览器
      • 再次请求,比较浏览器if-last-modified 和当前资源修改时间,相等则命中协商缓存,返回响应码304,反之返回路径对应的最新资源,和响应码200

      代码实现

      const http = require("http");
      const url = require("url");
      const fs = require("fs");
      const path = require("path");
      const mime = require("mime");
      
      
        let filePath = path.join(__dirname, "public", pathname);
        console.log(req.url);
        fs.stat(filePath, function (err, statObj) {
          if (err) {
            res.statusCode = 404;
            res.end("NOT FOUND");
          } else {
            if (statObj.isFile()) {
              // 判断 浏览器请求的文件路径 的change 时间 通过statObj.ctime
              const ctime = statObj.ctime.toUTCString();
              // 浏览器请求头if-modified-since ===文件上次的修改时间 ,命中协商缓存,则返回 304 浏览器缓存中请求资源
              if (req.headers["if-modified-since"] === ctime) {
                res.statusCode = 304; //去浏览器缓存中找
                res.end(); //
              } else {
                //  if-modified-since !==文件上次的修改时间,响应头Last-modified 设置 当前请求文件的 修改时间 做下次 浏览器请求的last-modify-since的对应值
                res.setHeader("Last-modified", ctime);
                fs.createReadStream(filePath).pipe(res);
                res.setHeader(
                  "Content-Type",
                  mime.getType(filePath) + ";charset=utf-8"
                );
              }
            } else {
              fs.access(htmlPath, function (err) {
                if (err) {
                  // 不可访问 设置 状态码404
                  res.statusCode = 404;
                  res.end("NOT FOUND");
                } else {
                  fs.createReadStream(htmlPath).pipe(res);
                  res.setHeader("Content-Type", "text/html;charset=utf-8");
                }
              });
            }
          }
        });
        // 写到这里 可以 nodemon cache2.js       启动服务 查看 http://localhost:3000/ 
      });
      server.listen(3000, () => {
        console.log("server start 3000");
      });
      
      

      效果展示

      每次刷新页面都会执行  console.log(req.url); 请求了服务器但服务器返回304 命中协商缓存 浏览器直接读取缓存资源即可

      成功

      总结

      jsjbwy
      下一篇:没有了