文件流
发布日期:2021-05-07 23:09:05 浏览次数:30 分类:精选文章

本文共 3781 字,大约阅读时间需要 12 分钟。

什么是文件流?

文件流(File Stream)是数据在计算机系统中从一个位置流动到另一个位置的过程。它类似于数据管道,允许程序在不将整个文件读入内存的情况下,逐块处理文件内容。

文件流的类型

  • 可读流(Readable)

    可读流用于从外部设备(如磁盘、网卡、显卡等)读取数据到内存。例如,磁盘上的文件通过可读流被读取到内存中供处理。

  • 可写流(Writeable)

    可写流用于将内存中的数据写入外部设备。例如,程序将内存中的数据通过可写流写入磁盘或其他外部存储设备。

  • 双工流(Duple)

    双工流允许数据在同一时间从外部设备流入内存,同时从内存流出到外部设备。这种机制在处理大文件时特别有用。


  • 为什么需要文件流?(节省内存)

    文件流的主要优势是节省内存,特别是在处理外部设备(如磁盘)与内存规模不一致的情况下。

    1. 外部设备与内存规模不一致

    • 内存小外部设备大:例如,内存只有1GB,但磁盘有2GB的数据。由于一次无法读完整个文件,需要通过文件流逐块读取和处理。

    2. 外部设备与内存处理速度不匹配

    • 内存处理速度快外部设备速度慢:例如,内存将1GB数据写入磁盘可能需要5秒,期间其他操作会受到影响。文件流允许磁盘在后台处理数据,而不会阻塞其他任务。

    3. 大文件读写效率低

    • 内存数据过多导致执行时间变长。文件流可以避免一次性读入或写出大文件,从而提高处理效率。

    文件可读流

    文件可读流(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, // 根据编码格式决定一次读取多少字符    autoClose: true, // 读取完成后自动关闭文件});

    可读流事件监听

    • open:文件打开时触发。
    • error:读取过程中发生错误。
    • close:文件读取完成。
    • end:文件读取完成,所有数据都已处理。
    • data:接收到文件块时触发。
    • pause:读取暂停时触发。
    • resume:读取恢复时触发。

    示例代码

    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,    autoClose: true,});rs.on("open", () => {    console.log("打开文件");});rs.on("error", (err) => {    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("恢复了");});

    文件可写流

    文件可写流(WriteStream)允许程序逐块向外部设备(如磁盘)写入数据,而不需要一次性加载整个文件到内存。

    创建可写流

    const fs = require("fs");const path = require("path");const filename = path.resolve(__dirname, "./3.txt");const ws = fs.createWriteStream(filename, {    encoding: "utf-8",    autoClose: true,});

    可写流事件监听

    • finish:写入数据完成。
    • error:写入过程中发生错误。
    • drain:写入完成,数据管道为空。

    示例代码

    const fs = require("fs");const path = require("path");const filename = path.resolve(__dirname, "./3.txt");const ws = fs.createWriteStream(filename, {    encoding: "utf-8",    autoClose: true,});ws.on("finish", () => {    console.log("写入完成");});ws.on("error", (err) => {    console.log("写入错误");});

    文件复制(对比流与不同读写方式)

    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);    const ws = fs.createWriteStream(to, {        autoClose: true,    });    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)

    通过pipe方法实现读取流到写入流。

    // 方法三:利用pipefunction method3() {    const from = path.resolve(__dirname, "./1.txt");    const to = path.resolve(__dirname, "./4.txt");    const rs = fs.createReadStream(from);    const ws = fs.createWriteStream(to);    console.time("管道");    rs.pipe(ws);    rs.on("close", () => {        ws.end();        console.timeEnd("管道");        console.log("复制完成");    });}method3();

    总结

    文件流是一种高效的数据处理机制,特别适用于处理大文件和外部设备与内存不匹配的情况。通过使用可读流和可写流,可以实现按需读取和写入,大幅节省内存资源,并提高处理效率。

    上一篇:前端常用设计模式
    下一篇:IO模块

    发表评论

    最新留言

    哈哈,博客排版真的漂亮呢~
    [***.90.31.176]2025年03月21日 06时47分58秒