Stream是Node.js中非常重要的一个模块,应用广泛。
先了解什么是流
流是一组有序的,有起点和终点的字节数据传输手段,是一个抽象接口,从而实现了从一个地方流到另一个地方。
Stream 有四种流类型:
- Readable - 可读的流 (例如fs.createReadStream()).
- Writable - 可写的流 (例如fs.createWriteStream()).
- Duplex - 双工流 (可读写的流)(例如 net.Socket).
- Transform - 变换流:在读写过程中可以修改和变换数据的 Duplex 流 (例如zlib.createDeflate()).
下面就一一介绍Stream的四种类型:
可读流(Readable streams)
Readable流提供了一种将外部来源的数据读入到应用程序的机制。
可读的流有两种模式:flowing (流动模式)和 paused(暂停模式)
在 flowing 模式下,可读流自动从系统底层读取数据,并通过 EventEmitter 接口的事件尽快将数据提供给应用,直到来源的数据耗尽
在 paused 模式下,必须显式调用 stream.read() 方法来从流中读取数据片段, 你不要它就在那儿耗着等你。暂停模式是一个可读流实例被创建时的默认模式
暂停模式和流动模式可以互相转换。
所有初始工作模式为 paused 的 Readable 流,可以通过下面三种途径切换到 flowing 模式:
- 监听 'data' 事件
- 调用 stream.resume() 方法
- 调用 stream.pipe() 方法将数据发送到 Writable
可读流可以通过下面途径切换到paused 模式:
- 如果不存在管道目标(pipe destination),可以通过调用 stream.pause() 方法实现。
- 如果存在管道目标,可以通过取消'data' 事件监听,并调用 stream.unpipe() 方法移除所有管 道目标来实现。
所有的流都是是基于事件eventEmitter,通过发射事件来反馈流的状态。 流的传输过程默认是以buffer的形式传输的,除非你给他设置其他编码形式.
我们来举一个简单的Readable的例子。
/**可读流**/
//首先建立一个1.txt文件,里面写有1234567890的字符
let fs = require('fs');//引入一个fs文件
//创建一个可读流
let rs = fs.createReadStream('./1.txt',{
flags:'r',//我们要对文件进行何种操作,r是读文件
encoding:'utf8',//设置编码
start:0,//从索引为0的位置开始读
end:7,//读到索引为8结束
highWaterMark:3// highWaterMark缓冲区大小,highWaterMark缓存区默认大小64kb
});
//先触发文件打开事件
rs.on('open',function () {
console.log('文件打开');
});
//监听它的data事件,当你一旦开始监听data事件的时候,流就可以读文件的内容并且发射data
//默认情况下,当你监听data事件之后,会不停的读数据,然后触发data事件,
//触发完data事件后再次读数据
rs.on('data',function (data) {
console.log(data);
rs.pause();//暂停读取和发射data事件
setTimeout(function(){
rs.resume();//一秒后恢复读取并触发data事件,直到文件读取完了。
},1000);
});
//将在流中有数据可供读取时触发
rs.on('readable',function(){
//读取一个字段,如果可读流发现你要读的字节小于等于缓存字节大小,则直接返回
//当你读完指定的字节后,如果可读流发现剩下的字节已经比最高水位线小了。
//则会立马再次读取填满 最高水位线
let char = rs.read(1);
console.log(char);
});
//如果读取文件中出错了,会触发error事件
rs.on('error',function (err) {
console.log(err);
});
//如果文件的内容读完了,会触发end事件
rs.on('end',function () {
console.log('读完了');
});
//如果文件的内容读完了,会触发close事件
rs.on('close',function () {
console.log('文件关闭');
});
/*输出结果
文件打开
123
4
4
56
7
7
8
读完了
文件关闭*/
复制代码
注:readable:在数据块可以从流中读取的时候发出。它对应的处理器没有参数,可以在处理器里调用read([size])方法读取数据。
read([size]):该方法可以接受一个整数作为参数,表示所要读取数据的数量,然后会返回该数量的数据。如果读不到足够数量的数据,返回null。如果不提供这个参数,默认返回系统缓存之中的所有数据。
可写流(Writable streams)
实现了stream.Writable接口的对象来将流数据写入到对象中
Writable流的write(chunk[,encoding] [,callback])方法可以把数据写入流中。
chunk是我们即将要写入的数据,通常是一个buffer或String对象
encoding设置指定字符串的编码格式
callback是在完成处理数据块后需要调用的函数。这是写数据成功与否的标志。若要发出故障信号,请用错误对象调用回调函数
write方法返回布尔值,默认是true,代表当前缓冲区(highWaterMark)的状态,如果缓冲区的状态已经到最高水位线,write
方法会返回false。一旦我们确认方法返回了false后,应该立刻停止调用write
方法,直到缓冲器中的数据被清空为止。此时会调用 'drain' 事件,在 'drain' 事件触发前, 不能写入任何数据块。
我们来举一个简单的Writable的例子。
let fs = require('fs');
//创建一个可写流
let ws = fs.createWriteStream('./1.txt',{
flags:'w',//我们要对文件进行何种操作
mode:0o666,
autoClose:true,//是否自动关闭文件
encoding:'utf8',//设置编码
start:0,//从第个索引开始写
highWaterMark:3// highWaterMark缓冲区大小,highWaterMark读取缓存区默认的大小64kb
});
let i = 6;
function write(){
//如果缓存区已满 ,返回false,如果缓存区未满,返回true
//如果能接着写,返回true,如果不能接着写,返回false
//按理说如果返回了false,就不能再往里面写了,但是如果你真写了,也不会丢失,会缓存在内存里。
//等缓存区清空之后再从内存里读出来
let flag = true;
while(flag && i>0){
flag = ws.write((i--)+'');
console.log('flag',flag);
console.log(i);
}
}
write();
ws.on('error',function () {
console.error('error');
});
ws.on('drain',function () {
console.log('drain');
write();
});
/*输出结果
flag true
5
flag true
4
flag false
3
drain
flag true
2
flag true
1
flag false
0
drain
*/
复制代码
小编先写到这里,后续在给大家介绍另两种方法
扩展