什么是流
- 流是指数据流动,数据从一个地方缓缓的流动到另一个地方
- 流是有方向的
- 可读流:Readable 数据从源头流向内存
- 可写流:Writable 数据从内存流向源头
- 双工流:Duplex 数据即可从源头流向内存,又可从内存流向源头
为什么需要流
- 其他介质和内存的数据规模不一致(内存大小不一致)
- 其他介质和内存的数据处理能力不一致
文件流
文件流:内存数据和磁盘文件之间的流动
文件流的创建
fs.createReadStream(path[,options])
创建一个文件可读流,用于读取文件内容
- path:读取的文件路径
- options:可选配置
- encoding:编码方式
- start:起始字节
- end:结束字节
- highWaterMark:每次读取的数量
- 如果encoding有值,该数量表示一个字符数
- 如果encoding为null,该数量表示字节数
- 返回:Readable的子类ReadStream
- 事件:rs.on(事件名,处理函数)
- open:文件打开事件(文件被打开后触发)
- error: 文件不存在时触发
- close:文件被关闭后触发(可通过rs.close手动关闭)或文件读取完成之后自动关闭
- data:读取到一部分数据后触发(注册data事件后,才会真正的开始读取)(每次读取highWaterMark指定的数量)(回调函数中会附带读取到的数据)
- end:文件读取完成之后会触发
- 事件:rs.on(事件名,处理函数)
js
const fs = require("fs");
const path = require("path");
const filename = path.resolve(__dirname, "./abc.txt");
fs.createReadStream(filename, {
encoding: "utf-8",
highWaterMark: 64 * 1024,
});
rs.on("open", () => {
console.log("文件被打开了");
});
rs.on("error", () => {
console.log("出错了!!");
});
rs.on("close", () => {
console.log("文件关闭了");
});
rs.on("data", chunk => {
console.log("读到了一部分数据:", chunk);
});
rs.on("end", ()=>{
console.log("全都读取完毕")
})
fs.createWriteStream(path[,options])
用于创建一个可写入的数据流(Writable Stream)。通过写入流,可以高效地将数据写入文件,特别适用于处理大文件或需要逐步写入数据的场景。
- path:写入的文件路径
- options
- flags:操作文件的方式
- encoding:编码方式
- start:起始字节
- highWaterMark:每次最多写入的字节数
- 返回:writable的字类WriteStream
- ws.on(事件名,处理函数)
- open:文件打开事件(文件被打开后触发)
- error: 文件不存在时触发
- close:文件被关闭后触发(可通过rs.close手动关闭)或文件读取完成之后自动关闭
- ws.write(data):写入一组数据
- data可以是字符串或Buffer
- 返回一个Boolean值(true:写入通道没有被填满,接下来的数据可以直接写入,无需排队)(false:写入通道目前已被填满,接下来的数据将写入队列,要特别注意背压问题,因为写入队列时内存中的数据,是有限的)
- 当写入队列清空时,会触发drain事件
- ws.end([data])
- 结束写入,将自动关闭文件(是否自动关闭取决于autoClose默认为true)
- data是可选的,表示关闭前的最后一次写入
- ws.on(事件名,处理函数)
js
const fs = require("fs");
const path = require("path");
const filename = path.resolve(__dirname, "./temp/abc.txt");
const ws = fs.createWriteStream(filename, {
encoding: "utf-8",
highWaterMark: 2
});
//ws.write("a");
//改进
let i = 0;
//一直写,直到到达上限,或无法再直接写入
function write() {
let flag = true;
while (i < 1024 * 1024 * 10 && flag) {
flag = ws.writs("a"); //写入a,得到下一次还能不能直接写
i++;
}
}
write();//但是这样只会写入最大的文字流数量,进入队列之后就不会再执行了
ws.on("drain", () => {
write();
});
js
复制文件
const fs = require("fs");
const path = require("path");
//方法1
async function method2() {
const from = path.resolve(__dirname, "./temp/abc.txt");
const to = path.resolve(__dirname, "./temp/abc2.txt");
console.time("方式1");
const content = await fs.promises.readFile(from);
await fs.promises.writeFile(to, content);
console.timeEnd("方式1");
console.log("复制完成");
}
//方法2
async function method2() {
const from = path.resolve(__dirname, "./temp/abc.txt");
const to = path.resolve(__dirname, "./temp/abc2.txt");
console.time("方式2");
const rs = fs.createReadStream(from);
const ws = fs.createWriteStream(to);
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("方式2");
console.log("复制完成");
})
}
rs.pipe(ws)
将可读流连接到可写流,返回参数的值,该方法可以解决背压问题(和方法二一样)