
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 基础教程,把 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 |
否 | 大文件 | 按块流动 |

💡 小提示:想直接动手试,文末「延伸学习」已放好对应的 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 的事件循环和异步机制补上,理解了“为什么同步会阻塞”,你才算真正吃透文件读取。
延伸学习
想把这块知识系统补齐,可以按这个顺序来:
- 先过一遍 Node.js 入门课程,把模块系统和 fs 基础铺平;
- 语法记不住时,翻 Node.js 文档教程 查具体 API 参数;
- 想深究异步底层,这篇 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]) 并行发起,整体耗时约等于最慢那一个,而不是三者相加。注意并发数太多要自己做限流。

TRAE-AI编程



