本文详解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程序设计有所帮助。