nodejs学习笔记07-Stream

fs.createReadStream,fs.createWriteStream

1. 认识 Stream

  • 什么是流呢?

    • 第一反应是流水,源源不断的流动;
    • 程序中的流也是类似的含义,从一个文件中读取数据时,文件的二进制(字节)数据会源源不断的被读取到程序中;
    • 而这个一连串的字节,就是程序中的流;
  • 所以,可以这样理解流:

  • 是连续字节的一种表现形式和抽象概念;

  • 流应该是可读的,也是可写的;

  • 通过 readFile或者 writeFile方式读写文件,为什么还需要流呢?

    • 直接读写文件的方式,虽然简单,但是无法控制一些细节的操作;
    • 比如从什么位置开始读、读到什么位置、一次性读取多少个字节;
    • 读到某个位置后,暂停读取,某个时刻恢复读取等等;
    • 或者这个文件非常大,比如一个视频文件,一次性全部读取并不合适;

2. 文件读写的Stream

  • 事实上Node中很多对象是基于流实现的

    • http模块的Request和Response对象;
    • process.stdout对象;
  • 官方:另外所有的流都是EventEmitter的实例

    • https://github.com/nodejs/node/blob/main/lib/stream.js

    • const Stream = module.exports = require('internal/streams/legacy').Stream;
      
      // internal/streams/legacy.js
      const EE = require('events');
      function Stream(opts) {
        EE.call(this, opts);
      }
      ObjectSetPrototypeOf(Stream.prototype, EE.prototype);
      ObjectSetPrototypeOf(Stream, EE);
      
      // events.js
      module.exports = EventEmitter;
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21



      - Node.js中有四种基本流类型
      - `Writable`:可以向其写入数据的流(例如 fs.createWriteStream())。
      - `Readable`:可以从中读取数据的流(例如 fs.createReadStream())。
      - `Duplex`:同时为Readable和的流Writable(例如 net.Socket)。
      - `Transform`:Duplex可以在写入和读取数据时修改或转换数据的流(例如zlib.createDeflate())



      ## Readable

      - 之前读取一个文件的信息

      ```js
      const fs = require("fs");
      fs.readFile("./a.txt",{encoding:"utf8"},(err,data)=>{
      // Hello World
      console.log(data);
      })
  • 这种方式是一次性将一个文件中所有的内容都读取到程序(内存)中,但是这种读取方式就会出现很多问题

    • 文件过大、读取的位置、结束的位置、一次读取的大小;
  • 这个时候,可以使用 createReadStream,更多参数可以参考官网

2.0.1. Readable的使用

  1. 创建文件的Readable

    1
    2
    3
    4
    5
    6
    7
    const fs = require("fs");
    const read = fs.createReadStream("./a.txt",{
    start: 3,
    end: 6,
    highWaterMark: 2,
    encoding: "utf-8"
    });
  2. 可以通过监听data事件,获取读取到的数据;

    1
    2
    3
    read.on("data",(data)=>{
    console.log(data);
    })

2.0.2. 其他操作

监听其他事件、暂停或者恢复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
read.on("data",(data)=>{
console.log(data);

read.pause();

setTimeout(()=>{
read.resume();
},1000);
});

read.on("open",()=>{
console.log("打开");
});
read.on("close",()=>{
console.log("关闭");
});

2.1. Writable

  • 之前写入一个文件的方式是这样的:

    1
    2
    3
    4
    const fs = require("fs");
    fs.writeFile("./b.txt","Hello",err=>{
    console.log(err);
    });
  • 这种方式相当于一次性将所有的内容写入到文件中,但是这种方式也有很多问题:

    • 比如希望写入内容,精确每次写入的位置等;
  • 这个时候,可以使用 createWriteStream,更多参数可以参考官网

2.1.1. Writable的使用

  1. 进行一次简单的写入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const write = fs.createWriteStream("./b.txt",{
    flags: 'a+'
    });

    write.write("Hello",err=>{
    if(err){
    console.log(err);
    }
    });
  2. 监听open事件

    1
    2
    3
    write.on("open",()=>{
    console.log("开始");
    });

2.1.2. close的监听

  • 并不能监听到 close 事件:

  • 这是因为写入流在打开后是不会自动关闭的;

  • 必须手动关闭,来告诉Node已经写入结束了;

  • 并且会发出一个 finish 事件的;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    const write = fs.createWriteStream("./b.txt",{
    flags: 'a+'
    });

    write.close();

    write.on("open",()=>{
    console.log("开始");
    });

    write.on("close",()=>{
    console.log("close关闭");
    });

    write.on("finish",()=>{
    console.log("finish关闭");
    });
  • 另外一个非常常用的方法是 end

    • end方法相当于做了两步操作:

      • write传入的数据和调用close方法;

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        const write = fs.createWriteStream("./b.txt",{
        flags: 'a+'
        });

        write.on("open",()=>{
        console.log("开始");
        });
        write.on("close",()=>{
        console.log("close关闭");
        });
        write.on("finish",()=>{
        console.log("finish关闭");
        });

        write.end("world");

2.1.3. pipe方法

  • 正常情况下,可以将读取到的输入流,手动的放到输出流中进行写入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const read = fs.createReadStream("./a.txt");

    const write = fs.createWriteStream("./copy.txt");

    read.on("data",data=>{
    write.write(data,err=>{
    if(err){
    console.log(err);
    }
    })
    });
  • 也可以通过pipe来完成这样的操作

    1
    2
    3
    4
    const read = fs.createReadStream("./a.txt");
    const write = fs.createWriteStream("./copy.txt");

    read.pipe(write);
本文结束  感谢您的阅读
  • 本文作者: Wang Ting
  • 本文链接: /zh-CN/2020/06/03/nodejs学习笔记07-Stream/
  • 发布时间: 2020-06-03 06:32
  • 更新时间: 2023-04-15 16:17
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!