Node.js 读取文件有哪几种方法?4 种完整写法一次讲清

编程狮(w3cschool.cn) 2026-09-30 07:04:55 浏览数 (14)
反馈

Node.js 读取文件四种方式封面

Node.js 读取文件最省事的做法是调用 fs 模块的 readFileSync,它同步返回文件内容;如果你的程序不能阻塞主线程,就用 fs.promises.readFile 配合 async/await。四种方式没有绝对优劣,只有适用场景不同:小文件求快用同步,服务里要保持异步用 Promise,老代码兼容用回调,超大文件必须上流式。今天这篇文章,编程狮就把这四种写法连同边界、坑点和选型逻辑一次性讲透,让你不但能写出能跑的代码,还能在面试里讲清“为什么大文件不能用 readFileSync”的底层原因。

本文基于 Node.js 16 LTS 实测,四种读取方式均给出可直接复制运行的示例。读完你会拿到一张选型表,并且清楚每种写法的内存占用、是否阻塞主线程、以及读取失败该怎么兜底。下面先把结论摆出来,再逐方法拆解。

先看结论

Node.js 读取文件有哪几种方法,本质是按“是否需要异步、文件有多大”两条线来选。先对号入座:

你的场景 推荐方式 一句话理由
读小配置文件、脚本一次性加载 fs.readFileSync 代码最短,同步拿到结果
想保持异步、又不想写回调嵌套 fs.promises.readFile + async/await 现代代码主流写法
兼容老代码、习惯回调风格 fs.readFile 回调里拿 err 和 data
文件很大或要边读边处理 fs.createReadStream 不占满内存,按块流动

Node.js 读取文件四种方式怎么选?

如果你还没装好 Node.js 环境,建议先过一遍 Node.js 基础教程,把 fs 模块和模块引入方式先铺平,后面读代码会更顺。

一、方法一:fs.readFileSync 同步写法

fs.readFileSync 是 fs 模块里最直接的一个方法,它会阻塞当前线程直到文件读完,然后把内容作为返回值交给你。语法上它不需要回调,也不返回 Promise,直接用变量接住就行。

const fs = require('fs');

// 第二个参数是编码,不写则返回 Buffer
try {
  const data = fs.readFileSync('w3cschool-demo.txt', 'utf8');
  console.log('同步读取到的内容:', data);
} catch (err) {
  console.error('读取失败:', err.message);
}

上面这段做了三件事:引入 fs 模块、调用 readFileSync 同步拿内容、用 try/catch 兜住异常。读小文件时这是最省心的写法。

适用场景:它最适合启动阶段的配置加载、CLI 工具、或者一次性的脚本任务。这类场景本来就不在请求链路里,阻塞一下无所谓,代码越短越好。

边界与性能:readFileSync 的代价是“阻塞”——在事件循环里它会卡住整个 Node.js 进程。在服务器请求处理函数里用它,会让后续请求全部排队,高并发场景务必避免。编码参数传 'utf8' 返回字符串,不传则返回 Buffer,需要手动 toString() 转码。

⚠️ 注意:同步读取会卡住整个 Node.js 进程。在 HTTP 请求处理链里用它,会让后续请求全部排队,高并发场景务必避免。

二、方法二:fs.readFile 回调写法

fs.readFile 是异步版本,结果通过回调函数的两个参数返回:第一个是错误对象 err,第二个是文件内容 data。Node.js 约定“错误优先”,所以一定要先判断 err。

const fs = require('fs');

fs.readFile('w3cschool-demo.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('读取失败:', err.message);
    return;
  }
  console.log('异步读取到的内容:', data);
});

回调写法的优点是零依赖、和老代码风格一致,缺点是多个异步读取嵌套时会形成“回调地狱”。适合只做一次简单读取、又不想引入 async 语法的场景。

失败处理:务必先 if (err) 再使用 data,否则读到不存在的文件会直接让后面的逻辑拿到 undefined 而报错。回调里的 return 也很关键,命中错误后及时退出,避免继续执行。

边界说明:回调风格虽然经典,但在需要“读完 A 再读 B 再读 C”的顺序任务里,嵌套会迅速失控。如果项目还在迭代,优先考虑下面的 Promise 写法。

三、方法三:fs.promises.readFile 与 async/await

这是目前最推荐的现代写法。fs.promises 把回调式 API 包成了返回 Promise 的版本,配合 async/await 让异步代码读起来像同步代码。

const fs = require('fs').promises;

async function readDemo() {
  try {
    const data = await fs.readFile('w3cschool-demo.txt', 'utf8');
    console.log('用 Promise 读取到的内容:', data);
  } catch (err) {
    console.error('读取失败:', err.message);
  }
}

readDemo();

readFileSync 同步返回、fs.readFile 走回调、fs.promises.readFile 走 Promise——三者拿到的是同一份内容,区别只在“等待结果”的方式。需要并发读取多个文件时,用 Promise.all 把多个读取并行起来也很自然。写代码时想随时查参数,可翻 Node.js 速查手册。

验证要点:运行后控制台应打印出 w3cschool-demo.txt 的文本内容;若文件不存在,catch 会输出 ENOENT 错误,说明路径或文件名有误。

性能对比:和同步相比,Promise 写法不阻塞事件循环,适合放在服务请求里;和大文件流式相比,它仍是一次性读进内存,文件过大时该用流式的还是要用。

四、方法四:fs.createReadStream 流式读取

当文件体积大到无法一次性塞进内存(比如几百 MB 的日志),readFile 系列会直接撑爆内存。此时要用 fs.createReadStream 做流式读取,它按块(chunk)陆续把数据推给你,边读边处理。

const fs = require('fs');

const stream = fs.createReadStream('w3cschool-demo.txt', 'utf8');
let content = '';

stream.on('data', (chunk) => {
  content += chunk; // 每收到一块就追加
});
stream.on('end', () => {
  console.log('流式读取完成,总长度:', content.length);
});
stream.on('error', (err) => {
  console.error('读取失败:', err.message);
});

四种方式的横向对比:

方式 是否阻塞 适合文件 内存占用
readFileSync 是 小文件 一次性载入
fs.readFile 否 中小文件 一次性载入
fs.promises.readFile 否 中小文件 一次性载入
fs.createReadStream 否 大文件 按块流动

readFileSync 与 promises.readFile 返回形式对比

💡 小提示:想直接动手试,文末「延伸学习」已放好对应的 Node.js 在线教程入口,不用本地装环境也能跑通示例。

实操清单:前置、操作、验证与失败处理

为了让上面的代码在你机器上真正跑起来,按这份清单走一遍:

  • 前置(安装/配置):本机已安装 Node.js 16 及以上版本(用 node -v 检查),并在项目目录放一个真实的 w3cschool-demo.txt 文本文件作为读取对象。
  • 操作(命令/运行):把示例保存为 read-demo.js,在终端执行命令 node read-demo.js 启动读取。
  • 验证(检查/预期):预期输出是文件原文;如果出现 ENOENT,说明文件不在当前工作目录,改用绝对路径重试即可。
  • 失败处理(失败/修复):读取大文件报内存溢出时,立刻从 readFileSync 切换到 fs.createReadStream;编码乱码则确认写入和读取都统一用 'utf8'。

总结

Node.js 读取文件共有四种主路径:readFileSync 同步最短、fs.readFile 回调最经典、fs.promises.readFile 配合 async/await 是现代主流、createReadStream 则专门应对大文件。Node.js 读取文件有哪几种方法,答案就落在“同步还是异步、文件大不大”这两个判断上。选型时先问自己两个问题——文件大不大、能不能阻塞主线程,答案就出来了。

要点带走:

  • 小文件、脚本一次性加载,优先用 readFileSync;
  • 写服务、要异步,优先用 fs.promises.readFile;
  • 大文件千万别用 readFileSync,改用流式读取;
  • 不论哪种写法,都要用 try/catch 或 err 判断兜住读取失败。

下一步建议把 Node.js 的事件循环和异步机制补上,理解了“为什么同步会阻塞”,你才算真正吃透文件读取。

延伸学习

想把这块知识系统补齐,可以按这个顺序来:

  1. 先过一遍 Node.js 入门课程,把模块系统和 fs 基础铺平;
  2. 语法记不住时,翻 Node.js 文档教程 查具体 API 参数;
  3. 想深究异步底层,这篇 Node.js 事件循环 讲得更透,值得延伸阅读。

常见问题

Q:readFileSync 和 readFile 读出来的内容有区别吗?

A:没有本质区别,都是文件原文。区别只在“等待方式”:前者同步阻塞、直接给返回值,后者异步、结果放进回调。编码参数都传 'utf8' 时,返回的都是字符串。

Q:读取大文件为什么会内存溢出?

A:readFile 系列会一次性把整个文件读进内存再返回,文件超过可用内存就崩溃。改用 createReadStream 按块流动,内存只占一小块缓冲区,几百 MB 的文件也能稳稳处理。

Q:用 async/await 读取多个文件怎么提速?

A:把每个 fs.promises.readFile 调用当成独立 Promise,用 Promise.all([a, b, c]) 并行发起,整体耗时约等于最慢那一个,而不是三者相加。注意并发数太多要自己做限流。

0 人点赞