fs
约 823 字大约 3 分钟
2026-01-14
fs 是 Node.js 内置的文件系统模块,提供对 文件 与 目录 的创建、读取、写入、删除、监视等能力。异步 API 由 libuv 驱动,适合 I/O 密集场景。
提示
服务端优先使用 Promise/回调版本,避免 readFileSync 等同步 API 阻塞事件循环。
使用方式
ESM
import * as fs from 'node:fs'
import { readFile } from 'node:fs/promises'CommonJS
const fs = require('node:fs')
const { readFile } = require('node:fs/promises')注
推荐使用 node: 前缀,能明确标识为 Node.js 内置模块。
API 形态
fs 提供三套接口:回调、Promise、同步。名称上通常以 Sync 结尾表示同步版本。
选择建议
- 回调:历史代码兼容、需要与流/事件风格混用。
- Promise:现代写法,配合
async/await可读性最佳。 - 同步:脚本启动阶段或 CLI 小工具里可用,服务端请求路径中慎用。
读取与写入文件
Promise
import { readFile, writeFile, appendFile } from 'node:fs/promises'
const text = await readFile('note.txt', 'utf8')
await writeFile('copy.txt', text)
await appendFile('log.txt', `${Date.now()}\n`)Callback
const fs = require('node:fs')
fs.readFile('note.txt', 'utf8', (err, text) => {
if (err) throw err
fs.writeFile('copy.txt', text, (writeErr) => {
if (writeErr) throw writeErr
})
})Sync
import { readFileSync, writeFileSync } from 'node:fs'
const text = readFileSync('note.txt', 'utf8')
writeFileSync('copy.txt', text)提示
不传 encoding 时返回 Buffer;传入 utf8 等编码会直接返回字符串。
注意
读取超大文件时不要用 readFile,请使用流式读取。
目录操作
import { mkdir, readdir, rm, rename } from 'node:fs/promises'
await mkdir('logs', { recursive: true })
const entries = await readdir('logs', { withFileTypes: true })
for (const entry of entries) {
if (entry.isFile()) console.log(entry.name)
}
await rename('logs', 'archive')
await rm('archive', { recursive: true, force: true })常用目录 API
mkdir:创建目录(recursive: true会级联创建)。readdir:读取目录项(withFileTypes: true返回Dirent)。rm/rmdir:删除目录(rm可替代rmdir)。rename:移动或重命名。
文件信息与权限
import { stat, lstat, access } from 'node:fs/promises'
import { constants } from 'node:fs'
const info = await stat('package.json')
console.log(info.isFile(), info.size, info.mtime)
await access('secret.txt', constants.R_OK)相关信息
stat 会跟随软链接,lstat 返回链接本身的信息。
提示
fs.constants 中包含常用权限位:R_OK、W_OK、X_OK 等。
文件描述符与 FileHandle
当需要 随机读写 或频繁操作同一文件时,先 open 再复用句柄更高效。
import { open } from 'node:fs/promises'
const file = await open('data.bin', 'r')
try {
const buffer = Buffer.alloc(16)
await file.read(buffer, 0, 16, 0)
console.log(buffer)
} finally {
await file.close()
}流式读写
import { createReadStream, createWriteStream } from 'node:fs'
import { pipeline } from 'node:stream/promises'
await pipeline(
createReadStream('video.mp4'),
createWriteStream('backup.mp4')
)提示
pipeline 会自动处理背压并在出错时正确关闭流,优于手写 pipe。
监视文件变化
import { watch } from 'node:fs'
const watcher = watch('src', { recursive: true }, (eventType, filename) => {
console.log(eventType, filename)
})
setTimeout(() => watcher.close(), 10_000)注意
fs.watch 在不同平台的行为可能不一致;需要轮询方案时可用 watchFile。
常见错误码
文件系统常见错误
ENOENT:路径不存在。EEXIST:目标已存在(常见于mkdir)。EACCES/EPERM:权限不足。ENOTDIR/EISDIR:期望文件却是目录,或相反。
- 先判断路径是否存在或权限是否足够。
- 需要覆盖时考虑
force: true或先删除再写入。 - 捕获异常并根据
err.code做分支处理。
小结
fs 覆盖了文件与目录操作的核心能力。日常开发中,优先 Promise API、大文件用流、构建路径用 path 模块,能显著提升可读性与稳定性。