释义
stream 流
stream 可以每次传一小段数据,数据是二进制的 Buffer,不是字符串
pipe 管道
通过事件实现管道
pipe 实际上是监听第一个流的 data 事件,然后写到第二个流。
// stream1 一有数据就塞给 stream2
stream1.on('data',(chunk)=>{'
stream2.write(chunk)
})
// stream1 停了,就停掉 stream2
stream1.on('end',()=>{
stream2.end()
})
Stream 对象的原型链
s = fs.createReadStream(path)
那么它的对象层级为:
1、自身属性(由 fs.ReadStream 构造);
2、原型:stream.Readable.prototype;
3、二级原型:stream.Stream.prototype;
4、三级原型:events.EventEmitter.prototype;
5、四级原型:Object.prototype.
Stream 对象都继承了 EventEmitter ,所以可以 on / emit 等。
支持的事件和方法
stream 分类
1、Readable :可读;
2、Writable :可写;
3、Duplex :可读可写(双向);
4、Transform :可读可写(变化)
- duplex 和 transform 的区别:
duplex 读到的数据和写的数据,一般不是同一个数据;transform 读写的是同一个数据,但是将数据做了一个转换
Readable Stream
- 静止态 paused 和流动态 flowing 1、默认处于 paused 态;
2、添加 data 事件监听,它就变为 flowing 态;
3、删掉 data 事件监听,它就变为 paused 态;
4、pause() 可以将它变为 paused ;
5、resume() 可以将它变为 flowing 。
Writable Stream
- drain 流干事件 1、调用 stream.write(chunk) 的时候,可能会得到 false ;
2、false 的意思是写太快了,数据积压了;
3、这个时候就不能再写 write 了,要监听 drain ;
4、等 drain 事件触发了,才能继续 write ;
const fs = require('fs')
const writer = fs.createWriteStream('./big_file.txt')
function writeOneMillionTimes(writer, data) {
let i = 1000000;
function write() {
let ok = true;
do {
i--;
if (i === 0) {
// 最后一次!
writer.write(data);
} else {
// 看看是应该继续,还是等待。
// 不要传入回调,因为还没有完成。
ok = writer.write(data);
if(ok === false){
console.log("不能再写了")
}
}
} while (i > 0 && ok);
if (i > 0) {
// 必须早点停下来!
// 等它排空时再写一些。
writer.once('drain', ()=>{
console.log("来点水")
write()
});
}
}
write();
}
writeOneMillionTimes(writer,'hello world')
- finish 事件 1、调用 stream.end() 之后;
2、而且缓冲区数据都已经传给底层系统之后;
3、触发 finish 事件。
创建自己的流,给别人用
创建一个 Writable Stream
stdin 会自动调用 write ,stdout 会自动调用 read
const {Writable} = require('stream')
const outStream = new Writable({
write(chunk,encoding,callback){
//传入什么,就输出什么
console.log(chunk.toString())
callback()
}
})
process.stdin.pipe(outStream)
创建一个 Readable Stream
1、将所有数据都 push 进去,然后 pipe
const {Readable} = require('stream')
const inStream = new Readable()
inStream.push('abcdefg')
inStream.push('hijklmn')
inStream.pipe(process.stdout)
2、数据按需供给,对方调用 read ,才会给一次数据
const inStream = new Readable({
read(){
const char = String.fromCharCode(this.currentCharCode++))
this.push(char)
console.log(`推了 ${char}`)
this.push(null)
}
})
inStream.currentCharCode = 65
inStream.pipe(process.stdout)
创建一个 Duplex Stream
const {Duplex} = require('stream')
const inoutStream = new Duplex({
write(chunk,encoding,callback){
console.log(chunk.toString())
callback()
},
read(){
const char = String.fromCharCode(this.currentCharCode++)
this.push(char)
if(this.currentCharCode > 90){this.push(null)}
}
})
inoutStream.currentCharCode = 65
process.stdin.pipe(inoutStream).pipe(process.stdout)
Transform Stream
const {Transform} = require('stream')
const upperCaseTr = new Transform({
transform(chunk,encoding,callback){
//将传入的数据变为大写
this.push(chunk.toString().toUpperCase())
callback()
}
})
process.stdin.pipe(upperCaseTr).pipe(process.stdout)
压缩
const fs = require('fs')
const zlib = require('zlib')
const file = process.argv[2]
fs.createReadStream(file)
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream(file + ".gz"))
//用 zlib 模块,将输入的文件压缩为 .gz 文件
转换文件,再压缩
const fs = require('fs')
const zlib = require('zlib')
const file = process.argv[2]
const {Transform} = require('stream')
const reportProgress = new Transform({
transform(chunk,encoding,callback){
//每传一次数据,就显示一个点。可以查看传了几次数据
//这里的代码变下,就可以无限操作数据,类似 webpack 的 loader
process.stdout.write('.')
callback(null,chunk)
}
})
fs.createReadStream(file)
.pipe(zlib.createGzip())
.pipe(reportProgress)
.pipe(fs.createWriteStream(file + '.gz'))
.on('finish',()=>console.log('done'))
加密压缩
const crypto = require('crypto')
...
fs.createReadStream(file)
//加密方式为 aes192,密钥为 123456
.pipe(crypto.createCipher('aes192','123456'))
.pipe(zlib.createGzip())
.pipe(reportProgress)
.pipi(fs.createWriteStream(file + '.gz'))
.on('finish',()=>console.log('done'))
//先加密,再压缩,不能先压缩再加密。