Stream流

121 阅读2分钟

第一个stream例子

const fs = require('fs');
const stream = fs.createWriteStream('./big_file.txt');

for (let i = 0; i < 1000000; i++) {
    stream.write(`这是第${i}行内容\n`);
}
stream.end();   //别忘记关闭stream
console.log('打完了');

分析

打开流,多次往里面塞内容,关闭流
就是可以多次往文件里写内容,且不覆盖上次的内容
最终得到一个24M的文件

Stream-流

image.png

stream就像是水管
stream.write就好像水龙头往水管里注入的水(数据)
每次写的小数据叫做chunk(块)
产生数据的水龙头叫做source(源头)
得到数据的那一段叫做sink(水池)

第二个stream例子

const fs= require('fs')
const http= require('http')
const server = http.createServer()
server.on('request',(request,response)=>{
fs.readFile('./big_file.txt',(err, data)=>{
  if (err)throw err
  response.end(data)
  console.log("打完了")
})
})
server.listen(8888)

Node.js会占用大量内存

第三个stream例子

const fs= require('fs')
const http= require('http')
const server = http.createServer()
server.on('request',(request,response)=>{
   const stream = fs.createReadStream("./big_file.txt")
    stream.pipe(response)
})
server.listen(8888)

分析

Node.js占用较低的内存
文件stream和response stream 通过管道相连

管道

两个流可以用一个管道
stream1 的末尾连接上stream2的开端
只要stream1有数据,就会流到stream2

image.png

常用代码

stream1.pipe(stream2)

链式操作

a.pipe(b).pipe(c)
等价于
a.pipe(b)
b.pipe(c)

管道可以通过事件去实现

//一有数据就塞给stream2
stream1.on('data',(chunk)=>{
    stream2.write(chunk)
})
//stream1停了,就停了stream2
stream1.on('end',()=>{
    stream.end()
})

Stream 对象的原型链

s=fs.createReadStream(path)
那么它的对象层级为
自身属性(由fs.ReadStream构造)
原型:stream.Readable.prototype
二级原型:stream.Stream.prototype
三级原型:events.EvebtEmitter.prototype
四级原型:Object.prototype

Stream对象都继承了EventEmitter

支持的时间和方法

Readable StreamWritable Stream
事件data ,end ,error ,close ,readabledrain ,finish ,error ,close ,pipe ,unpipe
方法pipe() ,unpipe(),wrap() ,destroy() ,read() ,unshift() ,resume() ,pause() ,isPaused() ,setEncodingwrite() ,destroy() ,end() ,crok() ,uncrok() ,setDefaultEncoding()

Stream分类

名称特点
Readable可读
Writable可写
Duplex可读可写(双向)
Transfor可读可写(变化)

Readable Stream

静止态paused和流动态flowing
默认处于paused态
添加data事件监听,它就变成flowing态
删掉data事件监听,它就变成paused态
pause()可以将它变成paused
resume()可以将它变为flowing

Writable Stream

drain 干枯事件

表示可以加点水了
我们调用stream.write(chunk)的时候,可能会得到false
false的意思表示写入太快,数据造成积压
是这个时候就不能继续写入了,要监听drain事件
等drain事件触发了,我们才能继续write

finish 事件

调用stream.end()之后,而且缓冲区的数据已经传给底层系统之后,再触发finish事件。

创建一个Writable Stream

const Writable = require('stream').Writable;

const outStream = new Writable({
    write(chunk, encoding, callback) {
        console.log(chunk.toString());
        callback();
    }
});

process.stdin.pipe(outStream);

创建一个Readable Stream

const Readable = require('stream').Readable;

const inStream = new Readable();

inStream.push('ABCDEFG');
inStream.push('abcdefg');
inStream.push(null);

inStream.pipe(process.stdout); //先push再调用
const Readable = require('stream').Readable;

const inStream = new Readable({
    read(size) {
        this.push(String.fromCharCode(this.currentCharCode++));
        if (this.currentCharCode > 90) {
            this.push(null);
        }
    }
});
inStream.currentCharCode = 65;
inStream.pipe(process.stdout);  //先调用再push

创建一个Duplex Stream

const Duplex = require('stream').Duplex;

const inoutStream = new Duplex({
    write(chunk, encoding, callback) {
        console.log(chunk.toString());
        callback();
    },
    read(size) {
        this.push(String.fromCharCode(this.currentCharCode++));
        if (this.currentCharCode > 90) {
            this.push(null);
        }
    }
});
inoutStream.currentCharCode = 65;
process.stdin.pipe(inoutStream).pipe(process.stdout);

创建Transform Stream

const Transform = require('stream').Transform;

const upperCaseTr = new Transform({
    transform(chunk, encoding, callback) {
        this.push(chunk.toString().toUpperCase());
        callback;
    }
});
process.stdin.pipe(upperCaseTr).pipe(process.stdout);