fs 文件系统模块
🎯 引言
学完这篇文章,你能用 Node.js 读取、写入、追加文件内容,创建和遍历目录,并理解 fs 模块三种 API 风格的区别,写出带完整错误处理的文件操作代码。
🧱 fs 模块是什么
fs(File System)是 Node.js 内置的文件系统模块,让 JavaScript 可以读写电脑上的文件和目录。这是浏览器里的 JS 做不到的事,也是后端开发的基本功,比如读取配置、保存日志、处理上传的文件,都离不开它。
本文统一使用推荐的 Promise 写法,引入方式如下:
import fs from 'node:fs/promises';
⚡ 三种 API 风格
同样是读文件,fs 模块提供了三种风格,先看对比再选:
// 1. 同步写法:代码会停在这里,等文件读完才往下走
import { readFileSync } from 'node:fs';
const data1 = readFileSync('./note.txt', 'utf-8');
// 2. 回调写法:多层嵌套时容易形成回调地狱
import { readFile } from 'node:fs';
readFile('./note.txt', 'utf-8', (err, data2) => {
console.log(data2);
});
// 3. Promise 写法(推荐):配合 async/await,代码从上往下读
import fs from 'node:fs/promises';
const data3 = await fs.readFile('./note.txt', 'utf-8');
🛠 常用文件操作
读取文件用 readFile:
import fs from 'node:fs/promises';
const content = await fs.readFile('./note.txt', 'utf-8');
console.log(content);
readFile 第二个参数一定要传 'utf-8' 编码。不传的话读到的是 Buffer 二进制数据,打印出来是一堆 <Buffer 48 65 ...>,这是初学者的高频困惑点。写入文件用 writeFile,追加内容用 appendFile:
import fs from 'node:fs/promises';
// 写入:文件不存在会创建,已存在会被覆盖
await fs.writeFile('./note.txt', '第一行内容');
// 追加:在文件末尾接着写,不会覆盖原有内容
await fs.appendFile('./note.txt', '\n第二行内容');
🛠 常用目录操作
import fs from 'node:fs/promises';
// 创建目录,recursive: true 表示父目录不存在时一并创建
await fs.mkdir('./logs/2024', { recursive: true });
// 读取目录,返回文件和子目录名称的数组
const files = await fs.readdir('./logs');
console.log(files); // ['2024']
// 删除文件或目录
await fs.rm('./logs/2024', { recursive: true });
来一个综合运用的小例子:找出当前目录下所有 .txt 文件并打印文件名。
import fs from 'node:fs/promises';
const files = await fs.readdir('./');
for (const file of files) {
if (file.endsWith('.txt')) {
console.log('找到文本文件:', file);
}
}
🪤 别忘了错误处理
文件操作充满不确定性:文件可能不存在、可能没有权限。Promise 写法下如果出错又不处理,异常会直接让进程崩溃退出。规范的写法是用 try/catch 包起来:
import fs from 'node:fs/promises';
try {
const content = await fs.readFile('./not-exist.txt', 'utf-8');
console.log(content);
} catch (err) {
console.log('读取失败:', err.message);
}
这样即使文件不存在,程序也只是打印一条友好的错误信息,然后继续运行。
🧾 小节总结
fs是 Node.js 内置的文件系统模块,负责文件和目录的读写操作。- 三种风格中推荐
node:fs/promises配合 async/await,不阻塞且代码清晰。 - 常用方法:
readFile读、writeFile覆盖写、appendFile追加、mkdir建目录、readdir读目录、rm删除。 readFile记得传'utf-8',否则读到的是 Buffer 二进制数据。- 文件操作必须用
try/catch处理异常,避免进程崩溃。
❓ 知识问答
Q1:readFile 读出来是一堆数字怎么办?
那是 Buffer 二进制数据,说明忘了传编码。给 readFile 加上第二个参数 'utf-8',或者对结果调用 .toString()。
Q2:writeFile 会把原文件覆盖吗?
会。想保留原内容就往末尾写,请用 appendFile;或者给 writeFile 传 { flag: 'a' } 选项。
Q3:同步写法是不是一定不能用?
不是绝对。比如脚本启动时读一次配置文件,用 readFileSync 简单直接,是可以接受的。但在服务器处理请求的代码里要避免,它会阻塞其他请求。
Q4:怎么判断文件是否存在?
可以直接 try/catch 包住读取操作,出错即不存在;也可以用同步的 fs.existsSync('./note.txt'),它返回布尔值,适合只做判断的场景。
🧪 小练习
写一个日记工具:每次运行 node diary.js,就往 diary.txt 里追加一行带日期的内容,例如 2024-01-01:今天学会了 fs 模块。要求用 Promise 写法并处理异常。
import fs from 'node:fs/promises';
// 请在这里编写代码
🎉 恭喜你已经掌握文件读写技能!下一篇我们学习 path 路径模块,解决文件路径拼接的各种坑。
