zoukankan      html  css  js  c++  java
  • 学习笔记—Node之文件拷贝与分片读写

    日常的学习笔记,包括 ES6、Promise、Node.js、Webpack、http 原理、Vue全家桶,后续可能还会继续更新 Typescript、Vue3 和 常见的面试题 等等。


    在之前的文章中,我们曾讲过 fs模块 ,其中包含 readFilewriteFile 两个方法。其中 readFile 是读取文件, writeFile 是写入文件。

    下面我们来用这两个方法来实现一个简单的 拷贝操作

    const fs = require('fs');
    const path = require('path');
    
    fs.readFile(path.resolve(__dirname, './package.json'), (err, data) => {
    	if (err) return console.log('error', err);
    	fs.writeFile(path.resolve(__dirname, './test.json'), data, () => {
    		console.log('success');
    	})
    })
    

    这样我们的文件就被拷贝下来了,但是这种操作有一个问题。

    readFile 只适合操作小文件 (如 JS、Css 和 json 文件等)。但是对于一些大文件 (如 音频、视频 等) ,虽然也可以操作,但是可能会 淹没可用内存。 原因是因为 readFile 是将文件全部读取下来之后,再进行操作的。

    这样我们就需要一种新的概念 分片读写,用来操作大型文件。 而分片读写 也就是后端领域中,经常提及的 数据流 (Stream) 操作。

    大文件的分片读写

    参考文献 fs 文件系统 | Node.js API文档

    如果要使用 node 实现一套文件读写,我们需要用到 fs.openfs.readfs.writefs.close

    其根本的实现思路就是,将需要进行读写的文件进行 边读边写 的操作,这样我们就可以控制读写的 速率

    现在我们先创建一个目标文件 test.js(需要进行拷贝操作的文件),里面随便写一些内容,比如 1234567890

    现在我们就需要进行 读一写一(读一个数字写一个数字) 的操作,然后再产生一个名为 newTest.js 的文件。

    我们先来实现一套 单个文字 的操作流程,来了解一下读写操作的原理。

    let buf = Buffer.alloc(1); // 创建一个用来进行存储数据内存的 Buffer 类型
    // 读取 源文件 中的数据
    fs.open(path.resolve(__dirname, "test.js"), "r", function (err, rfd) { // fs 是 数字类型
      // 将读取到的数据写入到 buf 中。从第0个位置开始写入buf,写入长度为1个,然后从文件的第0个位置开始进行读取。
      fs.read(rfd, buf, 0, 1, 0, function (err, bytesRead) {
        console.log(bytesRead); // 读取到的字节长度
        // 读取到的第一文字以16进制的形式存入了 buf实例 中。
        console.log(buf); // <Buffer 31>
        // 打开 目标文件。
        fs.open(path.resolve(__dirname, "newTest.js"), "w", function (err, wfd) {
          // 数据已经被写入了
          console.log(rfd, wfd); // 3 4
          // 向文件中写入buf中的数据。从第0个位置开始进行读取,读取长度为1个,然后再写入到文件中。
          fs.write(wfd, buf, 0, 1, 0, function (err, bytesWritten) {
            console.log("success");
          });
        });
      });
    });
    

    现在我们可以根据这种方式,然后使用 发布订阅模式,封装一套 分片读写操作。

    /**
     *
     * @param {String} source 源文件
     * @param {String} target 目标文件
     * @param {Function} cb 回调函数
     */
    function copy(source, target, cb) {
      const BUFFER_SIZE = 3; // buffer的固定长度
      const PATH_SOURCE = path.resolve(__dirname, source);
      const PATH_TARGET = path.resolve(__dirname, target);
      let buf = Buffer.alloc(BUFFER_SIZE); // buffer实例
      let rOffset = 0; // 读取偏移量
      let wOffset = 0; // 写入偏移量
      fs.open(PATH_SOURCE, "r", function (err, rfd) {
        if (err) return cb(err);
        fs.open(PATH_TARGET, "w", function (err, wfd) {
          if (err) return cb(err);
          function next() {
            fs.read(rfd, buf, 0, BUFFER_SIZE, rOffset, function (err, bytesRead) {
              if (err) return cb(err);
              // 如果全部读取完毕,则关闭当前 读写操作
              if (bytesRead == 0) {
                let index = 0;
                let done = () => {
                  if (++index == 2) { cb(); }
                };
                fs.close(wfd, done);
                fs.close(rfd, done);
                return;
              }
              fs.write(wfd, buf, 0, bytesRead, wOffset, function (err, bytesWritten) {
                  if (err) return cb(err);
                  // 读取成功,并更新偏移量
                  rOffset += bytesRead;
                  wOffset += bytesWritten;
                  next();
                }
              );
            });
          }
          next();
        });
      });
    };
    // 执行封装好的方法
    copy("./test.js", "./newTest.js", function (err) {
      if (err) return console.log(err);
      console.log("copy success");
    });
    

    这样我们就完成了一套 简单的 分片读写操作。

    但是这种方式会出现 回调地狱 的问题,代码看起来非常难以阅读和维护。

    这时我们就需要通过 数据流 (Stream) 来进行读写操作。

    本篇文章由 莫小尚 创作,文章中如有任何问题和纰漏,欢迎您的指正与交流。
    您也可以关注我的 个人站点博客园掘金,我会在文章产出后同步上传到这些平台上。
    最后感谢您的支持!

  • 相关阅读:
    [独库骑行之行路难]行路难!
    [独库骑行之我们穿过草原]巴音布鲁克大草原
    [独库骑行之我们路过湖泊]天山的高山湖泊
    [Tips]通过retintolibc方法编写通用exp的一个小技巧
    [独库骑行之奇山异石]丹霞地貌和雅丹地貌
    [独库骑行之我们穿过草原]美丽的乔尔玛草原
    [独库骑行之我们路过沙漠]塔克拉玛干的边缘
    [独库骑行之我们路过森林]那拉提的山林
    大家新年快乐!
    记忆力衰退
  • 原文地址:https://www.cnblogs.com/moxiaoshang/p/15790905.html
Copyright © 2011-2022 走看看