NodeJs系列之stream流
Stream流
stream 流分为四种:
- Readable - 可读操作。
- Writable - 可写操作。
- Duplex - 可读可写操作.
- Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
data - 当有数据可读时触发。
end - 没有更多的数据可读时触发。
error - 在接收和写入过程中发生错误时触发。
finish - 所有数据已被写入到底层系统时触发。
流的基本概念
流的英文stream,流(Stream)是一个抽象的数据接口,Node.js中很多对象都实现了流,流是EventEmitter对象的一个实例,会输出以buffer为单位的数据,或者能够吸收数据的东西,它的本质就是让数据流动起来。如下图:
从图可以直观的看到,数据可以直接从源通过管道直接导入到目标中。这里的stream不只是只有nodejs才有,这是操作系统中基本操作方式。nodejs是提供了可以操作流的api。Linux中也有管道概念的命令 “|” 。
readfile、writefile文件读写对比
var fs = require("fs");
console.log("准备写入文件");
fs.writeFile('input.txt', '我是通 过fs.writeFile 写入文件的内容', function(err) {
if (err) {
return console.error(err);
}
console.log("数据写入成功!");
console.log("读取写入的数据!");
//这里读取的数据data为buffer类型数据,需要通过toString转为字符串
fs.readFile('input.txt', function (err, data) {
if (err) {
return console.error(err);
}
console.log("异步读取文件数据: " + data.toString());
});
});
上述代码是使用fs库中的readfile和writeFile方法异步读写文件 ,它是将整个文件一次性返回data,然后再去对数据进行操作或者输出。这里会存在一个问题,如果读取的文件data特别大的时候,比如达到上百M的时候,在响应大量用户并发请求的时候,程序可能会消耗大量的内存,这样可能造成用户连接缓慢的问题。并发过大的话,内存开销也会比较大。Stream就完美的避开了这个问题,stream的原理是一边读取一边返回。数据通过管道流动给客户端,减轻了服务器的压力。
Stream读写文件
var fs = require("fs");
// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');
// 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');
// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);
console.log("程序执行完毕");
上述代码为stream管道流对文件的读写,写起来会比readfile方便一些,首先创建一个可读数据流readStream,一个可写数据流writeStream,然后直接通过pipe管道把数据流转过去。这种使用stream的拷贝相比存文件的读写实现拷贝,性能要增加很多性能方面也会好很多,一边读取一边写入,数据像是通过一个管道流动了起来,这也就是stream流。
request和response
request和reponse一样,都是stream对象,可以使用stream的特性,二者的区别在于request是source类型,而response是dest类型,数据的流向是从request 也就是source到 response的target。
Stream的弊端
- 通过管道(rs.pipe(ws))写入文件的时候,不是进行append方式进行追加,而是直接覆盖。
- 已经关闭的流不能重复使用,需要重新创建流。
- pipe 方法返回的是目标数据流,如 a.pipe(b) 返回的是 b,因此监听事件的时候请注意你监听的对象是否正确,例子上a.pipe(b) 这里需要监听的是b
a.pipe(b).on(‘end’,function(){//这里是b end })