对于前端童靴来说,很少接触文件读读取和存储。对于后端童鞋来说,文件读写就像吃饭喝水一样了。通过对nodeJS的学习,前端也能了解文件读写的大致流程。
文件读取的大致流程
计算机储存文件是放在储存(硬盘)中的,如果我们要对文件进行操作(查看、修改等),计算机会将文件内容读取到内存中(内存就是你手机上6G、12G那个玩意儿),操作完成后再写入储存(大致是这样,细节不用纠结)。
nodeJS读取文件
nodeJS中读取文件,可使用fs内置模块中的方法
nodejs.org/dist/latest…
const fs = require('fs');
async function test() {
// 读取文件
const content = await fs.promises.readFile(fromSrc, 'utf-8');
// 写入文件
await fs.promises.writeFile(toSrc, content);
// 如此就完成了一次文件的读写(复制)
}
test();
IO流
读取小文件使用fs模块非常方便,但是如果文件很大,就会占用更多的内存,内存对计算机来说是非常宝贵的,内存不足会导致卡顿,甚至无法运行其他程序。
要解决这个问题很简单,只需要一点一点读取文件即可,每次读取文件的一部分,对内存的占用就小了
如果将读取的所有片段在内存中拼接成完整内容,然后再进行写入操作,那和全读全写一样了,没有意义。
所以,我们可以读一点,写一点。
文件片段读取输入的过程就像水流一样,这既是文件流(stream)。IO流“I”指input,“O”指output,既“输入输出流”。
nodeJS中创建文件流
创建可读流:fs.createReadStream(path[, options])
创建可写流:fs.createWriteStream(path[, options])
可读流
const path = require('path');
const fs = require('fs');
const filename = path.resolve(__dirname, './myFiles/test.txt')
const rs = fs.createReadStream(filename, {
encoding: 'utf-8',
highWaterMark: 1,
autoClose: true,
});
// 注册'data'事件后流才会开始读取数据,chunk为每次读取到的一点数据
rs.on('data', (chunk) => {
console.log('读到了一点数据', chunk);
rs.pause(); // 暂停
});
rs.on('pause', () => {
setTimeout(() => {
rs.resume();// 再开始
}, 100);
});
rs.on('open', () => {
console.log('文件打开了');
});
rs.on('end', () => {
console.log('文件读取完毕');
});
rs.on('close', () => {
console.log('文件关闭');
});
rs.on("error", () => {
console.log("出错了!!");
});
// 详细用法请查阅api,不多做介绍
可写流
const path = require('path');
const fs = require('fs');
const filename = path.resolve(__dirname, './myFiles/test2.txt');
const wr = fs.createWriteStream(filename, {
flags: 'w',
encoding: 'utf-8',
highWaterMark: 16 * 1024,
autoClose: true,
});
let i = 0;
function write() {
let flag = true;
while (flag && i < 1024 * 1024 * 3) {
// 解决背压问题
// 如果管道满了,不能再加入数据了wr.write('b')返回false,反之
flag = wr.write('b');
i++;
}
}
write();
// 解决背压问题, 当管道清空时会触发此事件
wr.on('drain', () => {
write();
});
// 详细用法请查阅api,不多做介绍
背压问题
磁盘写入数据的速度是远低于内存的,我们想象内存和磁盘之间有一个“管道”,“管道”中是“流”,内存的数据流入管道是非常快的,当管道塞满时,内存中就会产生数据背压,数据积压在内存中,占用资源。
解决方法:上文“可写流”注释部分已标明
读写合并
const fs = require('fs');
const path = require('path');
const from = path.resolve(__dirname, './myFiles/test2.txt');
const to = path.resolve(__dirname, './myFiles/test3.txt');
function case1() {
const rs = fs.createReadStream(from);
const wr = fs.createWriteStream(to);
rs.on('data', (chunk) => { // 一边读
let flag = wr.write(chunk); // 一边写
if (!flag) {
rs.pause(); // 暂停读取数据
}
});
wr.on('drain', () => { // 通道清空
rs.resume(); // 重新开始读取数据
});
}
function case2() {
const rs = fs.createReadStream(from);
const wr = fs.createWriteStream(to);
rs.pipe(wr);
}
// case1和case2效果一样
// case1();
case2();