概念
流是一组有序的,有起点和终点的字节数据传输手段,在Node.js中处理流式数据的抽象接口 它不关心文件的整体内容,只关注是否从文件中读到了数据,以及读到数据之后的处理
分类
- Writable- 可写入数据的流(例如fs.createWriteStream())。
- Readable- 可读取数据的流(例如fs.createReadStream())。
- Duplex- 可读又可写的流(例如net.Socket)。
- Transform- 在读写过程中可以修改或转换数据的Duplex流(例如zlib.createDeflate())
可读流
概念
可读流被用来为 I/O 源提供灵活的 API,也可以被用作解析器:
- 继承自 steam.Readable 类
- 并实现一个 _read(size) 方法
示例
const stream = fs.createReadStream('./file.txt')
let chunks = []
stream.on("data", (chunk) => { // chunk是Buffer类型
chunks.push(chunk)
})
stream.on("end", () => {
constcontent = Buffer.concat(chunks).toString()
console.log(content)
})
- data事件在传输过程中触发
- end事件在当流中没有数据时(数据已传完)触发
可读流状态
readable.readableFlowing 可能有三种状态:null false true
- 初始时为null
- 添加data事件后变为true
- 调用readable.pause()、readable.unpipe()、或接收到背压,会变为false,在这个状态下,为data事件绑定监听器状态不会切换到 true
pipe 与 unpipe
- 使用pipe时数据流会被自动管理,所以即使可读流更快,目标可写流也不会超负荷。
- pipe会返回目标流的引用,支持链式操作
- unpipe用于解绑之前绑定的可写流
const http = require('http')
const fs = require('fs')
const server = http.createServer()
server.on('request', (request, response) => {
const stream = fs.createReadStream('./file.txt')
stream.pipe(response)
})
server.listen(8888)
强烈建议使用pipe
pause 与 resume
流动态和静止态的切换,改变data事件是否触发
可写流
概念
可写的流可用于输出数据到底层 I/O:
- 继承自 stream.Writable
- 实现一个 _write 方法向底层源数据发送数据
drain
上面pipe提到,使用pipe时,数据流会被自动管理,不会出现写入流超负荷的情况。但如果没用pipe,使用普通的write,写的太快时,调用write() 会返回 false。后续,当可以继续写入时会触发 drain 事件
//....
server.on('request', (request, response) => {
const stream = fs.createReadStream('./file.txt')
stream.on("data", (chunk) => {
response.write(chunk)
})
stream.on("end", () => {
response.end()
})
response.on("drain", () => {
console.log('可以写了')
})
})
//...
触发drain一定会丢失数据?
当流还未被排空时,调用
write()会缓冲chunk,并返回false。 一旦所有当前缓冲的数据块都被排空了(被操作系统接收并传输),则触发'drain'事件。 建议一旦write()返回 false,则不再写入任何数据块,直到'drain'事件被触发。 当流还未被排空时,也是可以调用write(),Node.js 会缓冲所有被写入的数据块,直到达到最大内存占用,这时它会无条件中止。
write 与 end
write是写入数据到可写流,end表明写入完毕,之久不能再调用write了
finish
finish事件在调用end() 且缓冲数据都已传给底层系统之后触发
双工流
双工流允许发送和接收数据:
- 继承自 stream.Duplex
- 实现 _read 和 _write 方法
转换流
使用流改变数据为另一种格式,并且高效地管理内存:
- 继承自 stream.Transform
- 实现 _transform 方法
示例
使用流 + gzip
const http = require('http');
const fs = require('fs');
const zlib = require('zlib');
http.createServer((req, res) => {
res.writeHead(200, {
'content-encoding': 'gzip', // 编码格式告知
});
fs.createReadStream(`${__dirname}/index.html`)
.pipe(zlib.createGzip()) // gzip压缩
.pipe(res);
}).listen(8000);