流是一个抽象接口,在 Node 里被不同的对象实现。比如HTTP 服务器request和response对象都是流。
Stream分类
在nodejs中,有四种stream类型:
- Readable:用来读取数据,比如 fs.createReadStream()可读流。
- Writable:用来写数据,比如 fs.createWriteStream()可写流。
- Duplex:可读+可写,比如 net.Socket()。
- Transform:在读写的过程中,可以对数据进行修改,比如 zlib.createDeflate()(数据压缩/解压)。
Readable流
Readable主要包含以下方法和事件。
1、事件: readable:在数据块可以从流中读取的时候发出。
data:类似readable,不同之处在于,当数据的事件处理程序被连接时,流被转变成流动的模式,并且数据处理程序被连续的调用,直到所有数据都被用尽
end:当数据不再被提供时由流发出
close:当底层资源,如文件,已关闭时发出。
error:在接收数据中出错是发出。
2、方法:
read([size]):从流中读数据.数据可以是String、Buffer、null(下面代码会有),当指定size,那么只读仅限于那个字节数
setEncoding(encoding):设置read()请求读取返回String时使用的编码
pause():暂停从该对象发出的data事件
resume():恢复从该对象发出的data事件
pipe(destination,[options]):把这个流的输出传输到一个由deatination(目的地)指定的Writable流对象。options是一个js对象.例如:{end:true}当Readable结束时就结束Writable目的地。
unpipe([destination]):从Writale目的地断开这一对象。
let fs = require('fs');
// 读取的时候默认读 默认64k,encoding 读取默认都是buffer
let rs = fs.createReadStream('./2.txt', {
highWaterMark: 3, // 字节
flags:'r',
autoClose:true, // 默认读取完毕后自动关闭
start:0,
//end:3,// 流是闭合区间 包start也包end
encoding:'utf8'
});
// 默认创建一个流 是非流动模式,默认不会读取数据
// 我们需要接收数据 我们要监听data事件,数据会总动的流出来
rs.on('error',function (err) {
console.log(err)
});
rs.on('open',function () {
console.log('文件打开了');
});
// 内部会自动的触发这个事件 rs.emit('data');
rs.on('data',function (data) {
console.log(data);
rs.pause(); // 暂停触发on('data')事件,将流动模式又转化成了非流动模式
});
setTimeout(()=>{rs.resume()},5000)
rs.on('end',function () {
console.log('读取完毕了');
});
rs.on('close',function () {
console.log('关闭')
});
复制代码
Writable流
有读就会有写,毕竟是可逆的,它和readable一样也有一些事件和方法
1、方法
write(chunk,[encoding],[callback]):将数据写入流。chunk(数据块)中包含要写入的数据,encoding指定字符串的编码,callback指定当数据已经完全刷新时执行的一个回调函数。如果成功写入,write()返回true.
end([chunk],[encoding],[callback]):与write()相同,它把Writable对象设为不再接受数据的状态,并发送finish事件。
2、事件
drain:在write()调用返回false后,当准备好开始写更多数据时,发出此事件通知监视器。
finish:当end()在Writable对象上调用,所以数据被刷新,并不会有更多的数据被接受时触发
pipe:当pipe()方法在Readable流上调用,已添加此writable为目的地时发出
unpipe:当unpipe()方法被调用,以删除Writable为目的地时发出。
let fs = require('fs');
let ws = fs.createWriteStream('./2.txt', {
flags: 'w', // 默认文件不存在会创建
highWaterMark: 1, // 设置当前缓存区的大小
encoding: 'utf8', // 文件里存放的都是二进制
start: 0,
autoClose: true, // 自动关闭
mode: 0o666, // 可读可写
});
// drain的触发时机,只有当highWaterMark填满时,才可能触发drain
let i = 9;
function write() {
let flag = true;
while (flag && i >= 0) {
i--;
flag = ws.write('111'); // 987 // 654 // 321 // 0
console.log(flag)
}
}
write();
ws.on('drain', function () {
console.log('干了');
write();
});
复制代码
Duplex(双工流)
有了双工流,我们可以在同一个对象上同时实现可读和可写,就好像同时继承这两个接口。 重要的是双工流的可读性和可写性操作完全独立于彼此。这仅仅是将两个特性组合成一个对象。
let fs = require('fs');
let { Duplex } = require('stream');
class MyDuplex extends Duplex{
_read(){
this.push('hello');
this.push(null);
}
_write(chunk,encoding,callback){
console.log(chunk);
callback();
}
}
let r = new MyDuplex();
r.on('data',function (data) {
console.log(data);
});
r.write('hello');
复制代码
Transform(转换流)
Transform stream是Duplex stream的特例,也就是说,Transform stream也同时可读可写。跟Duplex stream的区别点在于,Transform stream的输出与输入是存在相关性的。
let fs = require('fs');
let { Transform } = require('stream');
class T1 extends Transform{
_transform(chunk,encoding,callback){
this.push('123');
this.push(null);
callback();
}
}
let t = new T1();
t.on('readable',function (data) {
console.log(123123)
console.log(data);
});
复制代码