深刻Nodejs模块fs - 文件系统操做

node 的fs文档密密麻麻的 api 很是多,毕竟全面支持对文件系统的操做。文档组织的很好,操做基本分为文件操做、目录操做、文件信息、流这个大方面,编程方式也支持同步、异步和 Promise。javascript

本文记录了几个文档中没详细描写的问题,能够更好地串联fs文档思路:html

  • 文件描述符
  • 同步、异步与 Promise
  • 目录与目录项
  • 文件信息
  • stream
🔍 关注公众号“心谭博客” / 👉 前往 xxoo521.com / 欢迎交流和指正

文件描述符

文件描述符是一个非负整数。它是一个索引值,操做系统能够根据它来找到对应的文件。前端

在 fs 的不少底层 api 中,须要用到文件描述符。在文档中,描述符一般用fd来表明。例如:fs.read(fd, buffer, offset, length, position, callback)。与这个 api 相对应的是:fs.readFile(path[, options], callback)java

由于操做系统对文件描述符的数量有限制,所以在结束文件操做后,别忘记 close:node

const fs = require("fs");

fs.open("./db.json", "r", (err, fd) => {
    if (err) throw err;
    // 文件操做...
    // 完成操做后,关闭文件
    fs.close(fd, err => {
        if (err) throw err;
    });
});

同步、异步与 Promise

全部文件系统的 api 都有同步和异步两种形式。算法

同步写法

不推荐使用同步 api,会阻塞线程编程

try {
    const buf = fs.readFileSync("./package.json");
    console.log(buf.toString("utf8"));
} catch (error) {
    console.log(error.message);
}

异步写法

异步写法写起来容易进入回调地狱。json

fs.readFile("./package.json", (err, data) => {
    if (err) throw err;
    console.log(data.toString("utf8"));
});

(推荐)Promise 写法

在 node v12 以前,须要本身借助 promise 封装:api

function readFilePromise(path, encoding = "utf8") {
    const promise = new Promise((resolve, reject) => {
        fs.readFile(path, (err, data) => {
            if (err) return reject(err);
            return resolve(data.toString(encoding));
        });
    });
    return promise;
}

readFilePromise("./package.json").then(res => console.log(res));

在 node v12 中,引入了 fs Promise api。它们返回 Promise 对象而不是使用回调。 API 可经过 require('fs').promises 访问。如此一来,开发成本更低了。promise

const fsPromises = require("fs").promises;

fsPromises
    .readFile("./package.json", {
        encoding: "utf8",
        flag: "r"
    })
    .then(console.log)
    .catch(console.error);

目录与目录项

fs.Dir 类:封装了和文件目录相关的操做

fs.Dirent 类:封装了目录项的相关操做。例如判断设备类型(字符、块、FIFO 等)。

它们之间的关系,经过代码展现:

const fsPromises = require("fs").promises;

async function main() {
    const dir = await fsPromises.opendir(".");
    let dirent = null;
    while ((dirent = await dir.read()) !== null) {
        console.log(dirent.name);
    }
}

main();

文件信息

fs.Stats 类:封装了文件信息相关的操做。它在fs.stat()的回调函数中返回。

fs.stat("./package.json", (err, stats) => {
    if (err) throw err;
    console.log(stats);
});

注意,关于检查文件是否存在:

  • 不建议在调用 fs.open()、 fs.readFile() 或 fs.writeFile() 以前使用 fs.stat() 检查文件是否存在。而是应该直接打开、读取或写入文件,若是文件不可用则处理引起的错误
  • 要检查文件是否存在但随后并不对其进行操做,则建议使用 fs.access()。

ReadStream 与 WriteStream

在 nodejs 中,stream 是个很是重要的库。不少库的 api 都是基于 stream 来封装的。例以下面要说的 fs 中的 ReadStream 和 WriteStream。

fs 自己提供了 readFile 和 writeFile,它们好用的代价就是性能有问题,会将内容一次所有载入内存。可是对于几 GB 的大文件,显然会有问题。

那么针对大文件的解决方案天然是:一点点读出来。这就须要用到 stream 了。以 readStream 为例,代码以下:

const rs = fs.createReadStream("./package.json");
let content = "";

rs.on("open", () => {
    console.log("start to read");
});

rs.on("data", chunk => {
    content += chunk.toString("utf8");
});

rs.on("close", () => {
    console.log("finish read, content is:\n", content);
});

借助 stream 的 pipe,一行快速封装一个大文件的拷贝函数:

function copyBigFile(src, target) {
    fs.createReadStream(src).pipe(fs.createWriteStream(target));
}

参考连接


👇扫码关注,查看「前端图谱」&「算法题解」,您的支持是我更新的动力👇

相关文章
相关标签/搜索