流是指数据的流动,数据从一个地方缓缓的流动到另一个地方。
磁盘空间大,价格相对低;内存容量小,价格相对高。
磁盘读存数据相对较慢,内存读取数据相对较快。
fs.createReadStream(path[, options]) 创建一个文件可读流,用于读取文件内容
path:读取的文件路径 options:可选配置 encoding:编码方式 start:起始字节 end:结束字节 highWaterMark:每次读取数量 如果encoding有值,该数量表示一个字符数 如果encoding为null,该数量表示字节数 返回:Readable的子类ReadStream 事件:rs.on(事件名, 处理函数) open 文件打开事件 文件被打开后触发 error 发生错误时触发 close 文件被关闭后触发 可通过rs.close手动关闭 或文件读取完成后自动关闭 autoClose配置项默认为true data 读取到一部分数据后触发 注册data事件后,才会真正开始读取 每次读取highWaterMark指定的数量 回调函数中会附带读取到的数据 若指定了编码,则读取到的数据会自动按照编码转换为字符串 若没有指定编码,读取到的数据是Buffer end 所有数据读取完毕后触发 rs.pause() 暂停读取, 会触发pause事件 rs.resume() 恢复读取,会触发resume事件 const fs = require("fs"); const path = require("path"); const filename = path.resolve(__dirname,"./test.txt"); const rs = fs.createReadStream(filename,{ encoding: "utf-8", highWaterMark: 1 }); rs.on("open",()=>{ console.log("文件被打开了。"); }); rs.on("error",()=>{ console.log("文件读取错误。");//例:读取了一个不存在的文件 }); rs.on("close",()=>{ console.log("文件关闭了。"); }); rs.close();//这里使用手动关闭,或者可以等文件读取完毕后自动关闭。 rs.on("data",chunk => { console.log("读取到的数据:",chunk); }); rs.on("end",() => { console.log("数据读取完毕。"); }); rs.on("data",chunk => { console.log("读取到的数据:",chunk); rs.pause(); }); rs.on("pause", () => { console.log("暂停了"); setTimeout(() => { rs.resume(); }, 1000); }); rs.on("resume", () => { console.log("恢复了"); });fs.createWriteStream(path[, options]) 创建一个写入流
path:写入的文件路径 options flags:操作文件的方式 w:覆盖 a:追加 其他 encoding:编码方式 start:起始字节 highWaterMark:每次最多写入的字节数 返回:Writable的字类WriteStream ws.on(事件名, 处理函数) open error close ws.write(data) 写入一组数据 data可以是字符串或Buffer 返回一个boolean值 true:写入通道没有被填满,接下来的数据可以直接写入,无须排队 false:写入通道目前已被填满,接下来的数据将进入写入队列 要特别注意背压问题,因为写入队列是内存中的数据,是有限的 当写入队列清空时,会触发drain事件 ws.end([data]) 结束写入,将自动关闭文件 是否自动关闭取决于autoClose配置 默认为true data是可选的,表示关闭前的最后一次写入 rs.pipe(ws) 将可读流连接到可写流 返回参数的值 该方法可解决背压问题这里的一些参数就不多说了,与前面可读相似。 以及一些绑定事件。
const fs = require('fs'); const path = require('path'); const filename = path.resolve(__dirname,"./abc.txt"); const ws = fs.createWriteStream(filename,{ encoding: 'utf-8', highWaterMark: 3 }); const flag = ws.write('1'); console.log(flag);ws.write(data),写入一组数据,data可以是字符串或Buffer 返回一个Boolean值: true:写入通道没有被填满,接下来的数据可以直接写入,无须排队 如上面代码,highWaterMark设置三个字节,传入的1占两个字节,所以表示通道未被占满,传下一个数据时会直接写入,无需排队。
const fs = require('fs'); const path = require('path'); const filename = path.resolve(__dirname,"./abc.txt"); const ws = fs.createWriteStream(filename,{ encoding: 'utf-8', highWaterMark: 4 }); const flag = ws.write('雷'); console.log(flag); const fs = require('fs'); const path = require('path'); const filename = path.resolve(__dirname,"./abc.txt"); const ws = fs.createWriteStream(filename,{ encoding: 'utf-8', highWaterMark: 4 }); const flag = ws.write('𠮷'); console.log(flag);在这里就可以看出,对于中文文字,有些是3个字节,有些是两个字节。 所以当通道的容量小于传入的数据大小,则会将后面的数据放在写入队列。 由于内存的读写速度高于磁盘的读写速度,一段时间后写入队列会不堪重负。(就是背压问题)
const fs = require('fs'); const path = require('path'); const filename = path.resolve(__dirname,"./abc.txt"); const ws = fs.createWriteStream(filename,{ encoding: 'utf-8', flags: 'w', highWaterMark: 4 }); let i = 0; function write(){ let flag = true; while(i < 1024*1024*1 && flag){ flag = ws.write("a"); i ++; } } write();如上方法,一旦通道饱和,即不再写入,那么如何做到将1M的内容完成呢? 这里node提供了一个事件:当写入队列清空时,会触发drain事件
function write(){ let flag = true; while(i < 1024*1024*1 && flag){ flag = ws.write("a"); i ++; } } write(); ws.on("drain",()=>{ write(); });最后耗时20s将1M空间写满a。
方法一
const fs = require("fs"); const path = require("path"); async function method1() { const from = path.resolve(__dirname, "./abc.txt"); const to = path.resolve(__dirname, "./abc2.txt"); console.time("方式1"); const content = await fs.promises.readFile(from); await fs.promises.writeFile(to, content); console.timeEnd("方式1"); console.log("复制完成"); } method1()方法二
async function method2(){ const from = path.resolve(__dirname, "./abc.txt"); const to = path.resolve(__dirname, "./abc3.txt"); console.time('方式二'); 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("方式二"); console.log("复制完成"); }) } method2();将可读流连接到可写流,返回参数的值,该方法可解决背压问题。
const fs = require("fs"); const path = require("path"); async function method2() { const from = path.resolve(__dirname, "./abc.txt"); const to = path.resolve(__dirname, "./abc4.txt"); console.time("方式2"); const rs = fs.createReadStream(from); const ws = fs.createWriteStream(to); rs.pipe(ws); rs.on("close", () => { console.timeEnd("方式2"); }); } method2();