上周学习了Buffer,而stream与Buffer是分不开的。今天来总结一下stream。stream模块可以通过以下方式引入,但对于只是简单在流中写入数据或从流中消费数据的应用来说,不需要直接实现流接口,也不需要进行下面的调用。
const stream = require('stream');
Stream,顾名思义,就是“流”的意思。可以分为四种类型。
- Readable-可读流,负责读取外部的数据,并把数据缓存到内部的Buffer数组;
- Writable-可写流,负责消费数据,从可读流里面获取到数据,然后对得到的trunk数据块进行处理;
- Duplex-双工流,可读可写;
- Transform-转换流,可读可写,在读写过程中可以修改和变化数据,但不保存数据。
可读流在工作中有两种模式:flowing和paused。初始时均为paused模式,必须显式调用read方法才能读取数据,可以通过监听‘data’事件或调用resume和pipe方法,切换到flowing模式。在flowing模式下,可以读取数据,并将数据提供给应用,可以通过调用pause和unpipe方法切换到paused模式。
下面分别在例子中介绍一下可读流和可写流的常用事件。
var fs = require('fs');
var readStream = fs.createReadStream('read.jpg');
var n = 0;
readStream
.on('data',function (chunk) {
// 流将数据传递给消费者时触发(流转换到flowing模式时触发)
console.log('data emits')
//数据通过流传递时,是以buffer的格式传递的
console.log(Buffer.isBuffer(chunk));
//输出读取的文件内容
//console.log(chunk.toString('utf8'))
n++;
//让流暂停
readStream.pause()
console.log('data pause');
//定时器,模拟异步的处理
setTimeout(function () {
console.log('data pause end');
//重新启动数据的传递
readStream.resume()
},10)
})
.on('readable', function () {
// 流中有数据可供读取时触发(返回可用数据),当到达流数据尾部时,也会触发(返回null),触发顺序在‘end’之前
console.log('data readable');
})
.on('end', function () {
// 流中没有数据可提供消费时触发
console.log('data ends')
console.log(n);
})
.on('close', function () {
// 流传输结束、关闭的时候触发,触发后,该流将不再触发任何事件。
console.log('data close')
})
.on('error', function (e) {
// 传输有错误时触发
console.log('data read error' + e);
})
在上面的例子中,当数据读取的快,但写入较慢时就会出现问题,所以要判断数据写完后再继续读取,如下面的例子。
var fs = require('fs');
//创建一个可读流和一个可写流
var readStream = fs.createReadStream('old.JPG');
var writeStream = fs.createWriteStream('new.JPG');
readStream.on('data', function (chunk) {
// 可写流的write方法向流中写入数据,返回布尔值,当流不在drain状态时,调用write会缓存数据块,并返回false,否则返回true。
if(writeStream.write(chunk) === false){
console.log('still cached')
readStream.pause(); //pause方法使flowing模式的流停止触发‘data’事件,切出flowing模式,可用的数据保存在内部缓存中
}
})
readStream.on('end', function(){
writeStream.end(); //调用后表示接下来没有数据要被写入,再调用write方法会导致错误
})
//调用write方法返回false后,在恢复写入数据到流的时候触发
writeStream.on('drain',function(){
console.log('data drain');
//重新触发‘data’事件,切换到flowing模式
readStream.resume();
})
同时,可读流提供了更简单的方法实现上面的功能
var fs = require('fs');
//使用pipe方法,将可写流切换到flowing模式并将所有数据传给可写流,数据流被自动管理,即使可读流较快,可写流也不会超负荷
fs.createReadStream('old.JPG').pipe(fs.createWriteStream('new.JPG'));