背景
文件读写是服务端常见的功能,本文的目标是学习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:可读权限 (
r或read) - 2:可写权限 (
w或write) - 1:可执行权限 (
x或execute)
例如:
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);
});