node.js之文件操作

111 阅读4分钟

背景

文件读写是服务端常见的功能,本文的目标是学习fs模块的api,并通过几个案例来掌握这些api。

fs模块下的api都有三种模式,异步回调函数、同步、promise,以下是分别用这三种api完成对文件的读取:

import fs from "fs";
import fsPromises from "fs/promises";

// 异步回调
fs.readFile("data.txt", { encoding: "utf8" }, (err, data) => {
    console.log("回调形式", data);
});

// 同步
const res = fs.readFileSync("data.txt", {
    encoding: "utf8",
});
console.log("同步形式", res);

// Promise
fsPromises.readFile("data.txt", { encoding: "utf8" }).then((data) => {
    console.log("Promise 形式", data);
});

操作文件理所当然应该是一个异步操作,promise在异步操作中的可读性、便捷性都在回调函数之上,因此本文后续的api介绍都只用promise模式进行介绍

api

读文件

fs.readFile(file, options),用于读取文件

  • file (string): 要读取的文件路径。

  • options (可选):

    • encoding (string): 返回内容的编码,默认是 null(返回 Buffer)。

    • flag (string): 文件打开模式,默认是 'r'

import fsPromises from "fs/promises";

fsPromises.readFile("data.txt", { encoding: "utf8" }).then((data) => {
    console.log("Promise 形式", data);
});

写文件

fs.writeFile(file, data[, options]),用于写文件

  • file: 要写入的文件的路径(字符串)。

  • data: 要写入文件的数据,可以是字符串、Buffer、TypedArray 或 DataView。

  • options (可选): 选项对象,支持以下属性:

    • encoding: 写入数据的编码,默认为 'utf8'

    • mode: 文件权限,默认为 0o666

    • flag: 文件打开模式,默认为 'w',可以设置为 'a' 以追加内容。

import fsPromises from "fs/promises";

fsPromises.writeFile("example.txt", "Hello, World!").then(() => {
    console.log("文件写入成功");
});

重命名

fs.rename(oldPath, newPath),用于重命名或者移动路径

  • oldPath,旧路径/名称
  • newPath,新路径/名称,路径不存在会报错
import fsPromises from "fs/promises";

fsPromises.rename("data.txt", "example.txt").then(() => {
    console.log("文件重命名成功");
});

fsPromises.rename("example.txt", "./data/example.txt").then(() => {
    console.log("文件重命名成功");
});

删除文件

fs.unlink(path),删除文件

  • path (string): 要删除的文件路径。
import fsPromises from "fs/promises";

fsPromises.unlink("example.txt").then(() => {
    console.log("文件删除成功");
});

文件是否存在

fs.access(filePath),读文件前可能需要判断文件是否存在,避免报错

async function checkFileExistence() {
    try {
        await fs.access(filePath); // 检查文件是否可访问
        console.log('文件存在,开始读取');
        const data = await fs.readFile(filePath, 'utf8');
        console.log(data);
    } catch (err) {
        console.log('文件不存在');
    }
}

读目录

fs.readdir(path, options)

  • path (string): 要读取的目录路径。

  • options (可选):

    • encoding (string): 返回文件名的编码,默认是 'utf8'

    • withFileTypes (boolean): 如果为 true,返回 fs.Dirent 对象,默认为 false,返回文件名或者目录名的字符串。

import fsPromises from "fs/promises";

fsPromises.readdir("data", { withFileTypes: true }).then((files) => {
    files.forEach((file) => {
        console.log({ file });
    });
});

// { file: Dirent { name: 'hello.txt', path: 'data', [Symbol(type)]: 1 } }
// { file: Dirent { name: 'js', path: 'data', [Symbol(type)]: 2 } }

创建目录

fs.mkdir(path, options),创建目录

  • path (string): 要创建的目录路径。

  • options (可选):

    • mode (number): 文件权限,默认是 0o777

    • recursive (boolean): 是否递归创建,默认是 false

import fsPromises from "fs/promises";

fsPromises.mkdir("data/javaScript", { recursive: true }).then(() => {
    console.log("目录创建成功");
});

删除目录

fs.rmdir(path, options),删除目录

  • path (string): 要删除的目录路径。
  • options (可选):
    • recursive (boolean): 是否递归删除,默认是 false(会报错如果目录不为空)。
import fsPromises from "fs/promises";

fsPromises.rmdir("data", { recursive: true }).then(() => {
    console.log("目录删除成功");
});

Dirent类

fs.readdir(path, { withFileTypes: true})的结果

  • path,当前的父目录名称
  • name,文件名
  • isDirectory(),是否为目录
  • isFile(),是否为文件

flag

flag 定义了文件打开的模式

  • r,以 只读 模式打开文件。如果文件不存在,会抛出错误
  • r+,以 读写 模式打开文件。如果文件不存在,会抛出错误
  • w,以 写入 模式打开文件,写入数据,如果文件存在会覆盖内容;如果文件不存在,则创建新文件
  • w+,以 读写 模式打开文件,写入数据,如果文件存在会覆盖内容;如果文件不存在,则创建新文件
  • a,以 追加 模式打开文件。如果文件不存在,则创建文件;如果文件已存在,则将内容追加到文件末尾
  • a+,以 读写追加 模式打开文件。如果文件不存在,则创建文件;如果文件已存在,则将内容追加到文件末尾

mode

mode 参数定义了文件或目录的权限设置,通常是一个 八进制数字,它控制了用户(owner)、用户组(group)和其他用户(others)的读、写和执行权限。

mode 是一个 3 位的八进制数,每一位代表一个用户的权限:

  • 第 1 位:文件所有者(Owner)的权限
  • 第 2 位:文件所在组(Group)的权限
  • 第 3 位:其他用户(Others)的权限

每一位可以是以下权限的组合:

  • 4:可读权限 (rread)
  • 2:可写权限 (wwrite)
  • 1:可执行权限 (xexecute)

例如:

  • 4 (read) + 2 (write) = 6 (read + write)
  • 4 (read) + 1 (execute) = 5 (read + execute)

常见的mode示例:

0o777:所有用户都具有读、写、执行权限(最大权限)

0o755:所有者具有读、写、执行权限,组和其他用户具有读、执行权限

0o644:所有者具有读、写权限,组和其他用户具有读权限

0o700:只有文件所有者具有读、写、执行权限,其他用户没有任何权限

0o644:文件所有者可以读和写,其他用户只能读取

demo

统计目录下的文件数量

async function countFiles(path) {
    let count = 0;
    let files = await fsPromises.readdir(path, { withFileTypes: true });
    for (const file of files) {
        if (file.isFile()) {
            count++;
        } else {
            count += await countFiles(`${path}/${file.name}`);
        }
    }

    return count;
}


countFiles("../../javascript").then((count) => {
    console.log(count);
});