Stream把较大的数据,拆成很小的部分,分步传输
一个对象只要部署了Stream接口,就可以从读取数据,或者写入数据。Node内部很多涉及IO处理的对象,都部署了Stream接口,比如HTTP连接、文件读写、标准输入输出等。
我们以fs.createReadStream为例
var readStream = fs.createReadStream('test.txt');
readStream
.on('data', function(chunk){
console.log('data emits');
console.log(Buffer.isBuffer(chunk));
console.log(chunck.toString('utf8'));
})
.on('readable', function(){
console.log('data readable');
})
.on('end', function(){
console.log('data ends');
})
.on('close', function(){
console.log('data close');
})
- 我们通过fs.createReadStrem创建了一个可读的stream实例
- 监听data事件,每读入(或写入)一段数据,就会触发一次data事件
- data事件回调函数参数是一个buffer
- readable事件在数据流能够向外提供数据时触发
- 全部读取(或写入)完毕,触发end事件
- 数据源关闭时,close事件被触发
- 当读取数据发生错误时,error事件被触发。
resume() pause()
pause()
pause方法使得流动态的数据流,停止释放data事件,转而进入暂停态。任何此时已经可以读到的数据,都将停留在系统缓存。
resume()
resume方法会使得“可读数据流”继续释放data事件,即转为流动态。
var readStream = fs.createReadStream('test.txt');
readStream
.on('data', function(chunk){
readStream.pause();
setTimeout(function(){
readStream.resume();
}, 100);
})
上面的例子,每次data事件的时候我们暂停了数据的获取, 100毫秒后恢复
fs.createWriteStream
createWriteStream方法创建一个写入数据流对象,该对象的write方法用于写入数据,end方法用于结束写入操作。
end方法用于终止“可写数据流”。
var out = fs.createWriteStream(fileName, { encoding: "utf8" });
out.write(str);
out.end();
实例:重写拷贝图片
var fs = require('fs');
var readStream = fs.createReadStream('logo.png');//使用视频测试
var writeStream = fs.createWriteStream('logo_copy.png');
readStream
.on('data', function (chunk) {
writeStream.write(chunk);
})
.on('end', function (){
writeStream.end();//写完关闭写入流
})
上面代码有个问题: 如果读的速度比写的速度快,就会造成数据的丢失
所以我们我们在写数据的时候判断一下数据是否 处理完成,如果没有处理完成我们就暂停一下
然后writeStream监听'drain'事件 'drain'当缓存数据全部写入完成时触发
然后恢复读取
readStream
.on('data', function (chunk) {
if( writeStream.write(chunk) == false ){//当前写入没有完成
console.log('still cached');
readStream.pause();
}
})
.on('end', function (){
writeStream.end();//写完关闭写入流
})
readStream.on('drain', function(){
console.log('data drains');
readStream.resume();
})
Stream 种类
-
Readable
-
Writable
-
Duplex 双工(可读可写)
pipe()
可读流的方法 pipe方法是自动传送数据的机制,就像管道一样。它从“可读数据流”读出所有数据,将其写出指定的目的地。整个过程是自动的。
src.pipe(dst)
左边是可读流,右边是可写流
var fs = require('fs');
var readableStream = fs.createReadStream('file1.txt');
var writableStream = fs.createWriteStream('file2.txt');
readableStream.pipe(writableStream);
上面代码使用pipe方法,将file1的内容写入file2。整个过程由pipe方法管理,不用手动干预,所以可以将传送数据写得很简洁。
pipe方法返回目的地的数据流,如果目的地的数据流也可读的话,那么就可以使用链式写法,将多个数据流操作连在一起。,所以链式调用时,中间是双工流。
a.pipe(b).pipe(c).pipe(d)
// 等同于
a.pipe(b);
b.pipe(c);
c.pipe(d);
下面是一个例子。
var fs = require('fs');
var zlib = require('zlib');
fs.createReadStream('input.txt.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('output.txt'));
上面代码采用链式写法,先读取文件,然后进行压缩,最后输出。
可读数据流
var Readable = require('stream').Readable;
var rs = new Readable;
rs.push('beep ');
rs.push('boop\n');
rs.push(null);
rs.pipe(process.stdout);
可读数据流的push方法,用来将数据输入缓存。 rs.push(null)中的null,用来告诉rs,数据输入完毕。
可写数据流
//readable stream构造函数
var Readable = require('stream').Readable;
//writeable stream构造函数
var Writable = require('stream').Writable;
var readStream = new Readable();
var writStream = new Writable();
//读取数据
readStream.push('I ');
readStream.push('Hate ');
readStream.push('Nodejs \n');
readStream.push(null);//数据读取完成
//重写_write方法
writStream._write = function(chunk, encode, cb){
console.log( chunk.toString() );
cb();
}
readStream.pipe(writStream);