什么是文件流?
数据从一个地方流到另一个地方
可读流(Readable):外部设备(磁盘,网卡,显卡,打印机等等) --->>> 内存
可写流(Writeable):内存 --->>> 外部设备(磁盘,网卡,显卡,打印机等等)
双工流(Duple):内存 <<<---->>> 外部设备(磁盘,网卡,显卡,打印机等等)
为什么需要流?(节约内存)
- 外部设备与内存中的数据规模不一致 内存小 外部设备大 如果内存大小只有1G 但从磁盘读2G内 不能一次读完 这时需要流
- 外部设备与内存处理数据的能力不一致 内存处理数据快 外部设备慢 内存给磁盘写了1G 磁盘可能需要5秒去处理写数据 其他事件就会受到影响这时就需要流
- 读取或者写入大文件时数据会推挤在内存中,导致效率低(内存数据多,导致执行时间变长)
文件可读流
1.fs.createReadStream(filename,options) 创建可读流 返回一个ReadStream对象
2.ReadStream对象
const fs = require("fs");
const path = require("path");
const filename = path.resolve(__dirname, "./1.txt");
const rs = fs.createReadStream(filename, {
encoding: "utf-8",
highWaterMark: 1, //根据编码格式决定一次读多少 utf-8一次读一个字符
autoClose: true, //读完自动关闭文件
})
//这些事件都是异步执行的
rs.on("open", () => {
console.log("打开文件");
})
rs.on("error", () => {
console.log("出错了!!!"); //文件不存在或读取错误
})
rs.on("close", () => {
console.log("文件关闭"); //读完关 手动关
})
rs.on("end", () => {
console.log("文件读完了"); //文件读完触发
})
rs.on("data", (data) => {
console.log(data)//读文件
rs.pause(); //暂停读取
})
rs.on("pause", () => { //文件暂停读触发
console.log("暂停了");
setTimeout(() => {
rs.resume() //继续读
}, 1000)
})
rs.on("resume", () => { //文件恢复读触发
console.log("恢复了");
})
// rs.close(); //手动关闭文件
文件可写流
1.fs.createWriteStream(filename,options) 创建可写流
2.WriteStream 对象
3.当写入时可能会产生背压问题 由于内存的处理速度远远大于磁盘的写入速度,当从内存写入磁盘数据过多时,磁盘不能及时写入,导致通道拥挤,数据被阻塞在队列中,产生背压问题,消耗内存
4.解决背压问题
let i = 0
function write() {
let flag = true
while (i < 1024 * 1024 && flag) {
flag = ws.write("a");
i++;
}
}
write()
ws.on("drain", () => { //当通道清空时运行
write()
})
文件复制(对比流与不同读写的区别)
1.普通读写 10mb 文件复制 47ms
//文件复制
async function method1() { //先全读到内存,再从内存全部写入磁盘 消耗内存
const from = path.resolve(__dirname, "./1.txt");
const to = path.resolve(__dirname, "./2.txt");
console.time("方式一");
const data = await fs.promises.readFile(from);
await fs.promises.writeFile(to, data);
console.timeEnd("方式一");
console.log("复制完成");
}
method1()
2.文件流 10mb 文件复制 17ms
function method2() { //利用流
const from = path.resolve(__dirname, "./1.txt");
const to = path.resolve(__dirname, "./3.txt");
const rs = fs.createReadStream(from); //一次读64kb
const ws = fs.createWriteStream(to,{
autoClose:true,
}); //一次写16kb
console.time("方式二");
rs.on("data", (chunk) => {
const flag = ws.write(chunk)
if (!flag) { //如果产生背压
rs.pause() //暂停读
}
})
ws.on("drain", () => {
rs.resume() //管道为空时,恢复读
})
rs.on("close", () => {
ws.end(); //关闭写入流
console.timeEnd("方式二");
console.log("复制完成");
})
}
method2()
3.管道 10mb 文件复制 20ms
function method3() {
const from = path.resolve(__dirname, "./1.txt");
const to = path.resolve(__dirname, "./4.txt");
const rs = fs.createReadStream(from); //一次读64kb
const ws = fs.createWriteStream(to); //一次写16kb
console.time("管道");
rs.pipe(ws); //读取大文件时 与方法二一样 利用流 读取流--->写入流
rs.on("close", () => {
ws.end(); //关闭写入流
console.timeEnd("管道");
console.log("复制完成");
})
}
method3()