Node.js Stream模块自定义流实现指南

作者:岩茶时间:2026-10-24 03:44:44浏览:417

本文详解Node.js中Stream模块的四种基本流类型,通过继承Readable、Writable、Duplex和Transform类,分别演示了如何创建从数组读取数据的可读流、写入数据的可写流、双向操作的双工流以及进行数据转换的转换流,并提供完整代码示例。

本文通过具体实例,介绍如何在Node.js中利用Stream模块构建自定义流,供读者参考学习。

在处理特殊对象时,有时需要定制特定的数据流,Node.js提供了多种基础流类供开发者使用。

新建的流类必须继承自四个基础流类之一:stream.Writable、stream.Readable、stream.Duplex或stream.Transform,并务必调用父类的构造函数。

构建自定义可读流

一、构建自定义可读流

创建可读流需继承stream.Readable类,并实现readable._read()方法。

以下代码展示了一个从数组中读取数据的流实现方式。

从数组读取数据的可读流示例


const {Readable} = require('stream');
//这里我们自定义了一个用来读取数组的流
class ArrRead extends Readable {
  constructor(arr, opt) {
    //注意这里,需调用父类的构造函数
    super(opt);
    this.arr = arr;
    this.index = 0;
  }
  //实现 _read() 方法
  _read(size) {
    //如果当前下标等于数组长度,说明数据已经读完
    if (this.index == this.arr.length) {
      this.push(null);
    } else {
      this.arr.slice(this.index, this.index + size).forEach((value) => {
        this.push(value.toString());
      });
      this.index += size;
    }
  }
}
let arr = new ArrRead([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], {
  highWaterMark: 2
});
//这样当我们监听 'data' 事件时,流会调用我们实现的 _read() 方法往缓冲区中读取数据
//然后提供给消费者
arr.on('data', function (data) {
  console.log(data.toString());
});

构建自定义可写流

二、构建自定义可写流

实现可写流需继承stream.Writable类,并实现writable._write()方法。此外,writable._writev()方法为可选实现。

写入数组数据的可写流示例


const {Writable} = require('stream');
//这里我们自定义了一个用来写入数组的流
class ArrWrite extends Writable {
  constructor(arr, opt) {
    super(opt);
    this.arr = arr;
  }
  //实现 _write() 方法
  _write(chunk, encoding, callback) {
    this.arr.push(chunk.toString());
    callback();
  }
}
let data = [];
let arr = new ArrWrite(data, {
  highWaterMark: 3
});
arr.write('1');
arr.write('2');
arr.write('3');
console.log(data);

构建自定义可读可写流

三、构建自定义可读可写流

实现双向流需继承stream.Duplex类,并分别实现readable._read()和writable._write()方法。

双向操作的双工流示例


const {Duplex} = require('stream');
//这里我们自定义了一个用来写读可写数组的流
class ArrReadWrite extends Duplex {
  constructor(arr, opt) {
    super(opt);
    this.arr = arr;
    this.index = 0;
  }
  //实现 _write() 方法
  _write(chunk, encoding, callback) {
    this.arr.push(chunk.toString());
    callback();
  }
  //实现 _read() 方法
  _read(size) {
    //如果当前下标等于数组长度,说明数据已经读完
    if (this.index == this.arr.length) {
      this.push(null);
    } else {
      this.arr.slice(this.index, this.index + size).forEach((value) => {
        this.push(value.toString());
      });
      this.index += size;
    }
  }
}
let data = [];
let arrWR = new ArrReadWrite(data, {
  highWaterMark: 3
});
//往流中写入数据
arrWR.write('1');
arrWR.write('2');
arrWR.write('3');
console.log(data);
//往流中读取数据
console.log(arrWR.read(2).toString());
console.log(arrWR.read(2).toString());

构建自定义转换流

四、构建自定义转换流

转换流需继承stream.Transform类,并实现transform._transform()方法。

数据转换的转换流示例


const {Transform} = require('stream');
//这里我们自定义了一个用来转换数组的流
class Trans extends Transform {
  constructor(opt) {
    super(opt);
  }
  _transform(chunk, encoding, callback) {
    //将转换后的数据输出到可读流
    this.push(chunk.toString().toUpperCase());
    //参数一是Error对象
    //参数二如果传入,会被转发到 readable.push()
    callback();
  }
}
let t = new Trans({
  highWaterMark: 3
});
t.on('data', function (data) {
  console.log(data.toString());
});
t.write('a');
t.write('b');
t.write('c');

转换流的作用是对读取到的数据进行计算处理后输出,它兼具可读流和可写流的特性。

转换流与标准输入输出的管道连接


const {Transform} = require('stream');
//这里我们自定义了一个用来转换数组的流
class Trans extends Transform {
  constructor(opt) {
    super(opt);
  }
  _transform(chunk, encoding, callback) {
    //将转换后的数据输出到可读流
    this.push(chunk.toString().toUpperCase());
    //参数一是Error对象
    //参数二如果传入,会被转发到 readable.push()
    callback();
  }
}
let t = new Trans({
  highWaterMark: 3
});
t.on('data', function (data) {
  console.log('data', data.toString());
});
//stdin.pipe(t) 表示将我们的标准输入写入到我的转换流 t 中,此时 t 是可写流。
//pipe(process.stdout) 表示将转换流 t 中的数据读取到标准输出中,此时 t 是可读流。
process.stdin.pipe(t).pipe(process.stdout);

希望上述内容对Node.js程序设计有所帮助。

作者最新文章