Node.js Stream
Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。
Node.js,Stream 有四种流类型:
- Readable - 可读操作。
- Writable - 可写操作。
- Duplex - 可读可写操作.
- Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
- data - 当有数据可读时触发。
- end - 没有更多的数据可读时触发。
- error - 在接收和写入过程中发生错误时触发。
- finish - 所有数据已被写入到底层系统时触发。
从流中读取数据
创建 input.txt 文件,内容如下:
哈哈哈哈哈哈哈哈
创建 Stream.js 文件, 代码如下:
var fs = require("fs");
var data = '';
// 创建可读流
var readerStream = fs.createReadStream('input.txt');
// 设置编码为 utf8。
readerStream.setEncoding('UTF8');
// 处理流事件 --> data, end, and error
readerStream.on('data', function(chunk) {
data += chunk;
});
readerStream.on('end',function(){
console.log(data);
});
readerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序执行完毕");
Readable Stream 只读流
一个只读流有如下方法、成员、和事件
- Event: ‘data’
function(data){}
‘data’事件的参数是 Buffer(默认情况下),如果调用过 setEncoding()方法,则参数为一个字符串。
- Event:’end’
function(){}
此事件在流遇到 EOF(在 TCP 中为 FIN)时被触发,表示该流不会再有数据(不会再次触发’data’事件)。如果该流也是可写流,则它还可以继续写入。
- Event: ‘error’
function(exception){}
在收取数据出错时被触发。
- Event: ‘close’
function(){}
内部的文件描述符被关闭时被触发,并不是所有的流都会触发此事件。(例如,一个进入的(incoming)HTTP 请 求将不会触发’close’事件)
- Event: ‘fd’
function(fd){}
当数据流接收到文件描述符信息时触发该事件(一个文件数据流包含两部分信息:文件描述符信息和文件的数据信息)。 本事件只支持 Unix 数据流,其他类型的流不会触发该事件。
stream.readable
一个布尔值,默认为 true。当遇到错误或流读到结尾或者调用 destory()函数后,该值被设置为 false
stream.setEncoding(encoding)
该函数设置 data 事件返回字符串而不是 Buffer 对象。 编码类型可以设置为”utf8”,”ascii”或”base64”。
stream.pause()
暂停触发 data 事件。stream.resume()
恢复触发’data’事件。
stream.destroy()
关闭内部的文件描述符。 这样该流将不会再触发任何事件。
写入流
创建 StreamWrite.js 文件, 代码如下:
var fs = require("fs");
var data = '嘻嘻嘻嘻喜爱';
// 创建一个可以写入的流,写入到文件 output.txt 中
var writerStream = fs.createWriteStream('output.txt');
// 使用 utf8 编码写入数据
writerStream.write(data,'UTF8');
// 标记文件末尾
writerStream.end();
// 处理流事件 --> data, end, and error
writerStream.on('finish', function() {
console.log("写入完成。");
});
writerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序执行完毕");
Writable Stream 可写流
一个可写流具备以下方法、成员、和事件。
- Event: ‘drain’
function(){}
在一个 wrire() 方法被调用并返回 false 后触发,表明可以安全的再次写入该 stream。
- Event: ‘error’
function(exception){}
在异常发生赤错误时被触发。
- Event: ‘close’
function(){}
当底层的文件描述符已终止时发出。
- stream.writeable
一个boolean值,缺省为true,但是在一个’error’产生或是end()/destroy()被调用后,会变为false stream.write(string, encoding='utf8',[fd])
使用指定的编码将字符串字符串写入到流中。如果字符串已被刷新到内核缓冲区,返回 true。返回 false 则表明 内核缓冲区已满,数据将在未来被发送出去。'drain'
事件用来通知内核缓冲区何时为空。此方法的默认编码为'utf8'
。
如果指定了可选参数 fd,它将被当做一个文件描述符并通过流来发送。它只支持 UNIX 流,否则会被忽略且没 有任何提示。当用这种方式发送文件描述符时,在流清空之前关闭文件描述符可能导致发送出非法的描述符。stream.write(buffer)
同上,除了使用一个原始缓冲区。stream.end()
通过EOF或FIN来终止流。stream.end(string, encoding)
根据指定的编码发送字符串,并通过EOF或FIN来终止流。这对于减少发送数据包的数量是非常有用的。stream.end(buffer)
同上,但使用一个缓冲区。stream.destroy()
终止底层的文件描述符,此后流不再发出任何事件。
管道流
管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
如上面的图片所示,我们把文件比作装水的桶,而水就是文件里的内容,我们用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程。
以下实例我们通过读取一个文件内容并将内容写入到另外一个文件中。
设置 input.txt 文件内容如下:
哈哈哈哈哈哈哈哈
创建 pipe.js 文件, 代码如下:
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("程序执行完毕");
链式流
链式是通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。
接下来我们就是用管道和链式来压缩和解压文件。
创建 compress.js 文件, 代码如下:
var fs = require("fs");
var zlib = require('zlib');
// 压缩 input.txt 文件为 input.txt.gz
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'));
console.log("文件压缩完成。");
执行完以上操作后,我们可以看到当前目录下生成了 input.txt 的压缩文件 input.txt.gz。
接下来,让我们来解压该文件,创建 decompress.js 文件,代码如下:
var fs = require("fs");
var zlib = require('zlib');
// 解压 input.txt.gz 文件为 input.txt
fs.createReadStream('input.txt.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('input.txt'));
console.log("文件解压完成。");