Node.js fs 常用 API 整理:node:fs/promises、node:fs、fs 到底怎么用

Node.js 里操作文件,最常见的三个导入方式是:

复制代码
import * as fs from 'node:fs/promises';
import fs from 'node:fs';
import fs from 'fs';

这几个不是一回事。平时写项目,别乱用,直接按场景选。


1. 先记结论

日常优先用:

复制代码
import * as fs from 'node:fs/promises';

需要文件流、监听文件、同步 API 时,再用:

复制代码
import { createReadStream, createWriteStream, watch } from 'node:fs';

老代码里看到这个也能用:

复制代码
import fs from 'fs';

但新代码更推荐写:

复制代码
import fs from 'node:fs';

原因很简单:node: 前缀明确表示这是 Node.js 内置模块。


2. 三种导入方式的区别

写法 主要用途 是否推荐
node:fs/promises Promise 版文件操作,配合 async/await 日常首选
node:fs 完整 fs 模块,包括回调、同步、流、监听等 特定场景使用
fs 旧写法,通常等价于 node:fs 能用,但新代码不优先

3. 推荐写法

普通文件操作:

复制代码
import * as fs from 'node:fs/promises';

大文件流式处理:

复制代码
import { createReadStream, createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream/promises';

路径处理一般配合:

复制代码
import path from 'node:path';

常用组合:

复制代码
import * as fs from 'node:fs/promises';
import path from 'node:path';

4. 常用 API 速查

4.1 读取文件:readFile

适合读取文本、JSON、小文件。

复制代码
import * as fs from 'node:fs/promises';

const content = await fs.readFile('./a.txt', 'utf-8');

console.log(content);

读取 JSON:

复制代码
const content = await fs.readFile('./user.json', 'utf-8');
const user = JSON.parse(content);

console.log(user);

注意:

复制代码
const buffer = await fs.readFile('./logo.png');

不传编码时,返回的是 Buffer


4.2 写入文件:writeFile

文件不存在会创建,文件存在会覆盖。

复制代码
import * as fs from 'node:fs/promises';

await fs.writeFile('./a.txt', 'hello node', 'utf-8');

写入 JSON:

复制代码
const user = {
  name: 'Tom',
  age: 18,
};

await fs.writeFile(
  './user.json',
  JSON.stringify(user, null, 2),
  'utf-8'
);

注意:writeFile 默认是覆盖,不是追加。


4.3 追加内容:appendFile

适合写日志。

复制代码
import * as fs from 'node:fs/promises';

await fs.appendFile('./log.txt', '用户登录\n', 'utf-8');

也可以用 writeFileflag

复制代码
await fs.writeFile('./log.txt', '用户登录\n', {
  encoding: 'utf-8',
  flag: 'a',
});

常见 flag:

复制代码
w  写入,存在则覆盖
a  追加,不存在则创建
r  只读

4.4 创建目录:mkdir

创建单层目录:

复制代码
await fs.mkdir('./uploads');

创建多层目录:

复制代码
await fs.mkdir('./uploads/images/avatar', {
  recursive: true,
});

真实项目里,一般都加:

复制代码
recursive: true

否则父级目录不存在时会报错。


4.5 读取目录:readdir

读取目录下的文件名:

复制代码
const files = await fs.readdir('./uploads');

console.log(files);

判断文件还是文件夹:

复制代码
const items = await fs.readdir('./uploads', {
  withFileTypes: true,
});

for (const item of items) {
  if (item.isFile()) {
    console.log('文件:', item.name);
  }

  if (item.isDirectory()) {
    console.log('目录:', item.name);
  }
}

这个比先 readdir 再一个个 stat 更方便。


4.6 获取文件状态:stat

判断文件类型、大小、修改时间。

复制代码
const stat = await fs.stat('./a.txt');

console.log(stat.size);          // 文件大小,单位 byte
console.log(stat.isFile());      // 是否是文件
console.log(stat.isDirectory()); // 是否是目录
console.log(stat.mtime);         // 最后修改时间

常见封装:

复制代码
async function getFileInfo(filePath) {
  const stat = await fs.stat(filePath);

  return {
    size: stat.size,
    isFile: stat.isFile(),
    isDirectory: stat.isDirectory(),
    modifiedAt: stat.mtime,
  };
}

4.7 判断文件是否存在:access

复制代码
try {
  await fs.access('./a.txt');
  console.log('文件存在');
} catch {
  console.log('文件不存在');
}

判断是否可读写:

复制代码
import { constants } from 'node:fs';

await fs.access('./a.txt', constants.R_OK | constants.W_OK);

但实际项目里,不要过度依赖"先判断,再操作"。

比如:

复制代码
await fs.access('./a.txt');
const content = await fs.readFile('./a.txt', 'utf-8');

这不是最稳的写法,因为文件可能在 access 之后、readFile 之前被删除。

更直接的方式是:

复制代码
try {
  const content = await fs.readFile('./a.txt', 'utf-8');
  console.log(content);
} catch (err) {
  console.log('读取失败:', err.message);
}

只删除文件,不删除目录。

复制代码
await fs.unlink('./a.txt');

常见写法:

复制代码
try {
  await fs.unlink('./a.txt');
  console.log('删除成功');
} catch (err) {
  console.log('删除失败:', err.message);
}

4.9 删除文件或目录:rm

删除文件:

复制代码
await fs.rm('./a.txt');

删除目录:

复制代码
await fs.rm('./uploads', {
  recursive: true,
  force: true,
});

参数说明:

复制代码
recursive: true  递归删除目录内容
force: true      文件不存在也不报错

现在删除目录优先用 rm,不用老的 rmdir


4.10 重命名 / 移动:rename

重命名文件:

复制代码
await fs.rename('./old.txt', './new.txt');

移动文件:

复制代码
await fs.rename('./a.txt', './backup/a.txt');

注意:目标目录必须存在,否则会报错。


4.11 复制文件:copyFile

复制代码
await fs.copyFile('./a.txt', './backup/a.txt');

默认情况下,如果目标文件存在,会被覆盖。


4.12 复制目录:cp

复制整个目录:

复制代码
await fs.cp('./public', './dist/public', {
  recursive: true,
});

适合复制静态资源、备份目录、构建脚本。


4.13 打开文件句柄:open

一般业务不常用,做底层文件操作时会遇到。

复制代码
const file = await fs.open('./a.txt', 'r');

try {
  const stat = await file.stat();
  console.log(stat.size);
} finally {
  await file.close();
}

重点:打开后要关闭。


5. 大文件处理:用 Stream

小文件可以用 readFile,大文件不要一把梭。

不要这样处理大文件:

复制代码
const data = await fs.readFile('./big.mp4');
await fs.writeFile('./copy.mp4', data);

这样会把整个文件读进内存。

大文件用流:

复制代码
import { createReadStream, createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream/promises';

await pipeline(
  createReadStream('./big.mp4'),
  createWriteStream('./copy.mp4')
);

常见场景:

复制代码
大文件复制
文件上传
文件下载
视频处理
日志处理

6. 监听文件变化:watch

复制代码
import { watch } from 'node:fs';

const watcher = watch('./src', (eventType, filename) => {
  console.log(eventType, filename);
});

// 停止监听
// watcher.close();

适合开发工具、自动构建、热更新。

但不要把它当成强业务依赖,不同系统环境下表现可能有差异。


7. 同步 API:readFileSync / writeFileSync

同步 API 一般以 Sync 结尾。

复制代码
import fs from 'node:fs';

const content = fs.readFileSync('./config.json', 'utf-8');

console.log(content);

写入:

复制代码
fs.writeFileSync('./a.txt', 'hello', 'utf-8');

同步 API 会阻塞后续代码执行。

适合:

复制代码
启动时读取配置
命令行脚本
构建脚本
学习测试

不适合:

复制代码
接口请求里频繁使用
高并发服务
大量文件读写

接口里更推荐:

复制代码
import * as fs from 'node:fs/promises';

app.get('/config', async (req, res) => {
  const content = await fs.readFile('./config.json', 'utf-8');

  res.send(content);
});

8. 常用 API 汇总表

API 来源 作用 常用程度
readFile node:fs/promises 读取文件
writeFile node:fs/promises 写入文件
appendFile node:fs/promises 追加内容
mkdir node:fs/promises 创建目录
readdir node:fs/promises 读取目录
stat node:fs/promises 获取文件状态
access node:fs/promises 检查访问权限
unlink node:fs/promises 删除文件
rm node:fs/promises 删除文件或目录
rename node:fs/promises 重命名 / 移动
copyFile node:fs/promises 复制文件
cp node:fs/promises 复制目录
open node:fs/promises 打开文件句柄
createReadStream node:fs 创建读取流
createWriteStream node:fs 创建写入流
watch node:fs 监听文件变化
readFileSync node:fs 同步读取
writeFileSync node:fs 同步写入

9. 真实项目常用封装

9.1 确保目录存在

复制代码
import * as fs from 'node:fs/promises';

export async function ensureDir(dirPath) {
  await fs.mkdir(dirPath, {
    recursive: true,
  });
}

9.2 写入文本文件

复制代码
import * as fs from 'node:fs/promises';
import path from 'node:path';

export async function writeText(filePath, content) {
  const dir = path.dirname(filePath);

  await fs.mkdir(dir, {
    recursive: true,
  });

  await fs.writeFile(filePath, content, 'utf-8');
}

使用:

复制代码
await writeText('./logs/app.log', '启动成功\n');

9.3 读取 JSON

复制代码
import * as fs from 'node:fs/promises';

export async function readJSON(filePath) {
  const content = await fs.readFile(filePath, 'utf-8');

  return JSON.parse(content);
}

使用:

复制代码
const config = await readJSON('./config.json');

9.4 写入 JSON

复制代码
import * as fs from 'node:fs/promises';
import path from 'node:path';

export async function writeJSON(filePath, data) {
  const dir = path.dirname(filePath);

  await fs.mkdir(dir, {
    recursive: true,
  });

  await fs.writeFile(
    filePath,
    JSON.stringify(data, null, 2),
    'utf-8'
  );
}

使用:

复制代码
await writeJSON('./data/user.json', {
  name: 'Tom',
  age: 18,
});

9.5 判断路径类型

复制代码
import * as fs from 'node:fs/promises';

export async function getPathType(filePath) {
  try {
    const stat = await fs.stat(filePath);

    if (stat.isFile()) return 'file';
    if (stat.isDirectory()) return 'directory';

    return 'other';
  } catch {
    return 'not_exists';
  }
}

使用:

复制代码
const type = await getPathType('./uploads');

console.log(type);

9.6 复制大文件

复制代码
import { createReadStream, createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream/promises';

export async function copyLargeFile(source, target) {
  await pipeline(
    createReadStream(source),
    createWriteStream(target)
  );
}

10. 最后记这个规则

普通文件操作:

复制代码
import * as fs from 'node:fs/promises';

大文件操作:

复制代码
import { createReadStream, createWriteStream } from 'node:fs';

同步脚本:

复制代码
import fs from 'node:fs';

旧写法:

复制代码
import fs from 'fs';

一句话:

复制代码
日常用 node:fs/promises;
需要流、监听、同步方法时用 node:fs;
fs 是旧写法,能看懂就行,新代码优先写 node:fs。
相关推荐
C+++Python1 小时前
C++ 常量全面讲解
java·开发语言·c++
江屿风1 小时前
C++图论基础拓扑排序经典OJ题流食般投喂
开发语言·c++·笔记·算法·图论
芯岭技术郦1 小时前
MS32C001‑C:极致成本 32 位 MCU
c语言·开发语言·单片机
nexustech1 小时前
simplejson:Python JSON 处理的备用引擎
开发语言·python·其他·json
雷工笔记1 小时前
MES系列48-MES 系统「质量管理」完整设计与实施方案
开发语言·javascript·ecmascript
LiuJun2Son1 小时前
Angular 快速入门:服务和依赖注入
前端·javascript·angular.js
ch.ju1 小时前
Java Programming Chapter 4——The difference between overloading and overwriting.
java·开发语言
kidding7231 小时前
BMI 健康测量仪工具类小程序
前端·微信小程序·小程序
KaMeidebaby1 小时前
卡梅德生物技术快报|兔单克隆抗体应用实战:禽源病原 IFA 检测全流程拆解
前端·人工智能·物联网·算法·百度