在 Node.js 中,提供了一个用于操作文件的模块,叫做 fs
模块(File System)。通过这个模块,我们可以读取文件内容,并将其返回给客户端。
- 读取文件资源「 用户数据、配置文件、图片、音频等 」并返回给客户端
- 应用于前端自动化工具中
操作文件
读取内容
import fs from 'node:fs/promises';
// const 文件内容 = fs.readFile(文件路径, 配置对象 | 文件编码)
// 1. 文件路径 => 必须提供文件路径。如果路径不存在,会直接抛出错误。
// 2. 第二个参数可以是一个配置对象,也可以是直接指定编码(可选)。
// + 如果省略第二个参数,默认返回的是 Buffer 对象(即二进制数据流)。
// + 如果指定编码(如 'utf-8'),返回的是字符串。
// + 如果指定配置对象,可以设置更详细的选项,例如编码和文件访问模式(flag)。
const users = await fs.readFile('./assets/user.json', {
encoding: 'utf-8', // 指定编码,返回字符串
flag: 'r' // 以只读模式打开文件
});
console.log(users);
配置对象
在 Node.js 的 fs
模块中,配置对象通常包含以下参数:
属性 | 说明 |
---|---|
encoding | 指定读取或写入文件时使用的字符编码。例如:'utf8' |
flag | 文件的操作模式,决定文件的读写行为 |
flag
flag
参数用于指定文件操作的模式,以下是常用的 flag
值及其含义:
fs.readFile
的默认flag
是'r'
(只读模式)。fs.writeFile
的默认flag
是'w'
(写入模式)。
Flag 值 | 描述 |
---|---|
'r' | 只读模式 + 如果文件已存在,可以读取文件内容; + 如果文件不存在,会抛出异常。 |
'r+' | 读写模式 + 如果文件已存在,可以读写文件内容; + 如果文件不存在,会抛出异常。 |
'w' | 写入模式 + 如果文件已存在,会覆盖文件内容; + 如果文件不存在,会创建新文件。 |
'w+' | 读写模式 + 如果文件已存在,可以读写文件内容(覆盖原内容); + 如果文件不存在,会创建新文件。 |
'a' | 追加模式 + 如果文件已存在,写入的内容会追加到文件末尾; + 如果文件不存在,会创建新文件。 |
'a+' | 读写追加模式 + 如果文件已存在,可以读写并追加内容; + 如果文件不存在,会创建新文件。 |
一般情况下
writeFile
只是用来新增内容,所以flag
一般设置为a
或w
即可readFile
只是用来读取内容,所以flag
一般设置为r
皆可open
是一个更底层的操作,允许开发者以多种模式打开文件。其具体行为取决于flag
的设置。
文件描述符
当运行一个 Node.js 程序时,它会启动一个进程。在操作系统的底层,会维护一个文件和资源的映射表。每个文件在打开时,系统会为其分配一个唯一的标识符,这个标识符被称为文件描述符(File Descriptor,简称 FD)。
在 Node.js 中,文件描述符是一个非负整数,用于标识进程中打开的文件或资源。
import fs from 'node:fs/promises'
// 打开文件
// fs.open(path, flags, mode)
// + path: 文件路径
// + flags: 文件的打开方式
// - 'r':只读模式(文件必须存在)
// - 'w':写入模式(文件不存在会创建新文件,若文件存在会清空内容)
// - 'a':追加模式(文件不存在会创建新文件,若文件存在会在末尾追加内容)
// + mode: 文件权限(与 Linux 文件权限完全一致)
// fs.open 的返回值是一个文件句柄(FileHandle)
// 文件句柄是一个包含文件描述符和操作对应文件方法的 JavaScript 对象
const fileHandle = await fs.open('./assets/user.json', 'r')
// 读取文件内容
const content = await fileHandle.readFile('utf-8') // 使用文件句柄读取文件内容,指定编码为 UTF-8
console.log(content)
// 获取文件状态(如文件大小 {size}、创建时间 {birthtime}、修改时间 {mtime} 等)
const fileStatus = await fileHandle.stat()
console.log(fileStatus)
// 注意:通过文件描述符操作文件时,文件读取完毕后不会自动关闭,需要手动关闭文件句柄
await fileHandle.close()
写入内容
import fs from 'node:fs/promises';
const users = [
{ name: '张三', age: 20 },
];
// 参数一 => 文件路径(字符串形式)
// 参数二 => 写入的内容(必须是字符串)
// 参数三 => 配置对象(可选,包含写入选项,如 `flag`)
fs.writeFile('./assets/user.json', JSON.stringify(users, null, 2), {
flag: 'w+',
});
操作目录
创建目录
在 Node.js 中,使用 fs
模块的 writeFile
或 writeFileSync
方法时,如果目标文件不存在,会自动新建文件。
但如果目标路径中的文件夹不存在,这些方法无法自动新建文件夹,需先使用 fs.mkdir
或 fs.mkdirSync
创建文件夹。
这里的 mkdir
是 “make directory”的缩写,表示创建文件夹。如果创建失败,通常是因为权限问题。大多数情况下,只要有权限,创建文件夹是可以成功的。
import fs from 'node:fs/promises';
// 创建文件夹
// 1. 参数一 => 文件夹路径 「如果文件夹已存在,会直接抛出错误」
// 2. 参数二 => 配置对象 「可选」
// + recursive => 如果中间文件夹不存在,是否自动创建
// - 默认值为 false。假设不存在 assets,会直接抛出错误。
// - 如果设置为 true,假设不存在 assets,会自动创建中间文件夹 assets 后再新建目录 test。
// + mode => 文件夹权限 「默认值为 0o777」=> 一个八进制值,表示文件夹的权限。
await fs.mkdir('./assets/test', { recursive: true });
读取目录
import fs from 'node:fs/promises';
const files = await fs.readdir('./assets')
// `files` 是一个字符串数组,包含文件夹中的所有文件和子文件夹的名称
// 但是,这种读取方式无法区分每个名称是文件还是文件夹。
console.log(files) // [ 'bar', 'foo', 'index.js' ]
import fs from 'node:fs/promises';
const files = await fs.readdir('./assets', { withFileTypes: true })
// 如果我们需要区分文件和文件夹,可以使用 `fs.readdir` 的 `withFileTypes` 参数, 加上后就变成了一个对象数组
// 对象数组类似于 Dirent { name: 'bar', parentPath: './assets', [Symbol(type)]: 2 }
// + name => 文件名
// + parentPath => 文件路径
// + [Symbol(type)] => 文件类型
files.forEach(async (file) => {
// `isDirectory()` 方法可以判断当前项是否是文件夹
if (file.isDirectory()) {
console.log(`${file.name}/`)
} else {
// `name` 属性则返回文件或文件夹的名称
console.log(file.name)
}
})
import fs from 'node:fs/promises';
// 如果文件夹中存在嵌套结构,比如子文件夹中还有文件夹,我们可以使用递归来读取所有内容
async function readDir(path) {
const files = await fs.readdir(path, { withFileTypes: true })
if (files.length) {
files.forEach(async (file) => {
if (file.isDirectory()) {
console.log(`${path}/${file.name}`)
await readDir(`${path}/${file.name}`)
} else {
console.log(`${path}/${file.name}`)
}
})
}
}
readDir('./assets')
判断目录是否存在
import fs from 'node:fs'
// 早期 Node.js 使用同步方法 existsSync 判断文件 或 目录是否存在
// 该方法会返回一个布尔类型值,用于标识目标目录或文件是否存在
const isExists = fs.existsSync('./uploads')
console.log(isExists) // true
import fs from 'node:fs/promises'
try {
// access 用于判断 文件 或 目录 是否存在
// 如果存在,则不报错
// 如果不存在,则报错
await fs.access('./uploads')
} catch (error) {
// 如果目录不存在,则创建目录
await fs.mkdir('./uploads')
}
重命名
import fs from 'node:fs/promises';
// 重命名操作,源文件不存在会报错
// 重命名文件夹
fs.rename('./assets/bar', './assets/bar-new')
// 重命名文件
fs.rename('./assets/file1.txt', './assets/foo-new.md')