Node.js 简介

fs 文件系统模块

掌握 Node.js fs 模块读写文件、操作目录的常用方法,理解同步、回调、Promise 三种写法的区别。

🎯 引言

学完这篇文章,你能用 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

read.js
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

write.js
import fs from 'node:fs/promises';

// 写入:文件不存在会创建,已存在会被覆盖
await fs.writeFile('./note.txt', '第一行内容');

// 追加:在文件末尾接着写,不会覆盖原有内容
await fs.appendFile('./note.txt', '\n第二行内容');

🛠 常用目录操作

dir.js
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 文件并打印文件名。

find-txt.js
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 包起来:

safe-read.js
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 写法并处理异常。

diary.js
import fs from 'node:fs/promises';

// 请在这里编写代码

🎉 恭喜你已经掌握文件读写技能!下一篇我们学习 path 路径模块,解决文件路径拼接的各种坑。