一.理解Stream
很多人第一次接触 Stream(流) 都会觉得抽象、难懂:到底什么是流?流又能用来做什么?
想象一下:你要把一个巨大鱼塘的水全部抽干。
现在给你两个工具:
一个小杯子,一个抽水机。
你会选哪个?
肯定选抽水机,对吧?
因为鱼塘太大了,用杯子一杯一杯舀,根本舀不完,还特别慢、特别占地方;
但抽水机不一样,它可以连续不断、一点一点把水抽走,不用一次性把所有水都装起来,也不会因为水太多而装不下。
这个抽水机,就是编程里的 Stream(流) 。
对应到代码里就是:
- 以前用
fs直接一次性读取/写入整个文件 就像用小杯子一次性把一池塘水全装起来。
文件小的时候没问题,但文件一大,内存直接爆掉。 - Stream(流) 就像抽水机 + 水管,它会把文件切成一小段一小段,源源不断、连续地读取或写入,不用一次性把整个文件加载到内存里。
大文件、视频、日志、网络数据……只要数据量大,都必须用 Stream,Stream 在 Node.js 里,是 EventEmitter 的实例
二.Stream的使用
在Node中很多功能都是基于流的比如Http,API之间的关系如下:
Node中有四种常见的流,但是其实常用的就只有前两个:
Writable :可以向其写入数据的流,例如fs.createWriteStream()
Readable :可以从中读取数据的流,例如fs.createReadStream()
Duplex :同时为Readable和Writable 例如 net.Socket
Transform :Duplex可以在写入和读取数据时修改或转换数据的流 例如zlib.createDeflate()
Readable
const fs = require("fs");
// 通过流读取文件
const readStream = fs.createReadStream('./aaa.txt',{
start:3,
end:20,
highWaterMark:3
})
// 监听到读取的数据
readStream.on('data',(data)=>{
console.log(data.toString());
readStream.pause() // 读取暂停
setTimeout(()=>{
readStream.resume(); // 读取恢复
},2000)
})
// 监听文件是否打开
readStream.on('open',(fd)=>{
console.log("通过流将文件打开了~",fd);
})
// 监听文件是否结束,会自动关闭文件流
readStream.on('end',()=>{
console.log("数据已经读取结束~");
})
// 文件读取结束并且关闭
readStream.on('close',()=>{
console.log("文件读取完毕并且关闭");
})
Writable
const fs = require("fs");
// 一次性写入内容
const writeStream = fs.createWriteStream('./ccc.txt',{
flags:'a+', // 写入方式
start:0, // 写入位置
encoding:'utf8', // 编码方式
})
// open事件
writeStream.on('open',(fd)=>{
console.log("文件已经被打开",fd);
})
// 使用可写流对文件进行写入
writeStream.write('kobe byrant',err=>{
console.log("写入成功!");
})
writeStream.on('finish',()=>{
console.log("文件写入完成~");
})
// end方法,将最后的内容写入到文件中,并关闭,涉及两个操作
writeStream.end('hhhhh')
// 当写入完成时候手动调用写入方法
writeStream.close()
pipe方法
const fs = require('fs');
// 传统拷贝方式
fs.readFile('./foo.txt',(err,data)=>{
console.log(data);
fs.writeFile('./ddd.txt',data,{
flag:'a+'
},(err,data)=>{
if(err) return
console.log(data,"打印数据信息");
})
})
// 使用可读流和可写流
const readStream = fs.createReadStream('./foo.txt')
const writeStream = fs.createWriteStream('./foo_copy.txt')
readStream.on('data',(data)=>{
writeStream.write(data)
})
readStream.on('end',()=>{
writeStream.close()
})
// 使用pip建立管道来进行文件拷贝
readStream.pipe(writeStream)