三套 API
fs 模块有三种风格:
import fs from 'fs'; // 回调 API
import fsSync from 'fs'; // 同步(同上面,加 Sync 后缀)
import fsp from 'fs/promises'; // Promise API ★ 推荐
Promise API(推荐)
import { readFile, writeFile, readdir, stat, mkdir, rm } from 'fs/promises';
// 读
const content = await readFile('config.json', 'utf8');
const json = JSON.parse(content);
// 写
await writeFile('output.txt', 'Hello');
await writeFile('output.txt', 'Hello', 'utf8');
await writeFile('binary.bin', Buffer.from([1, 2, 3]));
// 追加
import { appendFile } from 'fs/promises';
await appendFile('log.txt', 'new line\n');
// 列目录
const files = await readdir('./src');
const filesWithType = await readdir('./src', { withFileTypes: true });
// 看文件信息
const info = await stat('config.json');
console.log(info.size, info.isFile(), info.isDirectory());
// 建目录
await mkdir('./output', { recursive: true }); // 自动建中间层
// 删
await rm('./tmp', { recursive: true, force: true }); // -rf 风格
await rm('./file.txt'); // 删文件
同步 API(启动期 / 配置加载用)
import { readFileSync, writeFileSync, existsSync } from 'fs';
const config = JSON.parse(readFileSync('config.json', 'utf8'));
writeFileSync('output.txt', 'data');
if (existsSync('./tmp')) {
// ...
}
只在启动期 / CLI 工具用同步——服务运行期同步 = 阻塞事件循环。
检查文件是否存在
// ❌ 老式(有竞态条件)
if (existsSync('./file.txt')) {
await readFile('./file.txt'); // 这中间文件可能没了
}
// ✓ 直接尝试 + catch
try {
const data = await readFile('./file.txt', 'utf8');
} catch (err) {
if (err.code === 'ENOENT') {
// 不存在
} else {
throw err;
}
}
流式读写大文件
小文件用 readFile;大文件(几百 MB+)用流:
import { createReadStream, createWriteStream } from 'fs';
import { pipeline } from 'stream/promises';
await pipeline(
createReadStream('big.txt'),
createWriteStream('big-copy.txt'),
);
pipeline 自动处理错误 + 关流。详见 19-streams。
边读边处理(line-by-line)
import { createReadStream } from 'fs';
import { createInterface } from 'readline';
const rl = createInterface({
input: createReadStream('log.txt'),
crlfDelay: Infinity,
});
for await (const line of rl) {
if (line.includes('ERROR')) console.log(line);
}
观察文件变化
import { watch } from 'fs/promises';
(async () => {
const watcher = watch('./src', { recursive: true });
for await (const event of watcher) {
console.log(event.eventType, event.filename);
}
})();
实用例子
递归找文件
import { readdir } from 'fs/promises';
import { join } from 'path';
async function find(dir, pattern) {
const entries = await readdir(dir, { withFileTypes: true });
const results = [];
for (const e of entries) {
const full = join(dir, e.name);
if (e.isDirectory()) {
results.push(...await find(full, pattern));
} else if (pattern.test(e.name)) {
results.push(full);
}
}
return results;
}
const tsFiles = await find('./src', /\.ts$/);
或用 fs.glob(Node 22+)/ fast-glob 库。
复制目录
import { cp } from 'fs/promises';
await cp('./src', './dest', { recursive: true });
临时文件
import { mkdtemp } from 'fs/promises';
import { tmpdir } from 'os';
const tmp = await mkdtemp(join(tmpdir(), 'myapp-'));
// 用完 rm -rf
await rm(tmp, { recursive: true, force: true });
常用 stat 字段
const s = await stat('file.txt');
s.size // 字节
s.isFile()
s.isDirectory()
s.isSymbolicLink()
s.mtime // 最后修改
s.ctime // 元数据改动
s.atime // 访问时间
坑
- 同步 API 阻塞循环——服务运行期别用
readFile不带 encoding 返回 Buffer——文本要'utf8'- 路径分隔符跨平台用
path.join,别拼字符串 rm加force: true才不会因不存在报错(等于rm -f)
下一篇:path / os / process。