node.js基础(六):stream

308 阅读3分钟

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');
    })

  1. 我们通过fs.createReadStrem创建了一个可读的stream实例
  2. 监听data事件,每读入(或写入)一段数据,就会触发一次data事件
  3. data事件回调函数参数是一个buffer
  4. readable事件在数据流能够向外提供数据时触发
  5. 全部读取(或写入)完毕,触发end事件
  6. 数据源关闭时,close事件被触发
  7. 当读取数据发生错误时,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);