fs 模块
fs 全称为
file system,称之为文件系统,是 Node.js 中的内置模块,可以对计算机中的磁盘进行操作。
就是实现和硬盘的交互,比如下面的这些操作
本章节会介绍如下几个操作:
文件写入
文件读取
文件移动与重命名
文件删除
文件夹操作
查看资源状态
文件写入
文件写入就是将 数据 保存到 文件 中,我们可以使用如下几个方法来实现该效果
| 方法 | 说明 |
|---|---|
| writeFile | 异步写入 |
| writeFileSync | 同步写入 |
| appendFile / appendFileSync | 追加写入 |
| createWriteStream | 流式写入 |
writeFile 异步写入
语法: fs.writeFile(file, data[, options], callback)
参数说明:
file 文件名
data 待写入的数据
options 选项设置
(可选)callback 写入回调
返回值: undefined
代码示例:
// require 是 Node.js 环境中的 '全局' 变量,用来导入fs模块
const fs = require("fs");
// 将 [三人行,必有我师焉。] 写入到当前文件夹下的 [座右铭.txt] 文件中
fs.writeFile("./座右铭.txt", "三人行,必有我师焉。", (err) => {
// 如果写入失败,则回调函数调用时,会传入错误对象,此时err就会被写入错误对象;如写入成功,会传入 null
if (err) {
console.log(err);
return;
}
console.log("写入成功");
});若文件不存在,会自动创建文件并写入

调用异步 fs API 后,Node.js 会发起 I/O,当前 JavaScript 调用栈继续执行;I/O 完成后,回调才会在事件循环的相应阶段获得执行机会。不是等主线程所有代码执行完才开始 I/O,也不是调用时立刻把尚未完成的回调放入可执行队列。
writeFileSync 同步写入
语法: fs.writeFileSync(file, data[, options])
参数与 fs.writeFile 大体一致,只是没有 callback 参数
返回值:undefined
代码示例:
try {
fs.writeFileSync("./座右铭.txt", "三人行,必有我师焉。");
} catch (e) {
console.log(e);
}Node.js 中的磁盘操作是由其他
线程完成的,结果的处理有两种模式:
- 同步 API 会阻塞当前 JavaScript 线程直到操作完成,不适合请求处理等高并发路径;启动脚本、一次性工具中可能更简单且完全合理
- 异步 API 不阻塞事件循环,更利于并发处理,但并不保证单次操作更快,还会有调度和回调/Promise 成本
appendFile / appendFileSync 追加写入
appendFile作用是在文件尾部追加内容,appendFile语法与writeFile语法完全相同
语法:
fs.appendFile(file, data[, options], callback)
fs.appendFileSync(file, data[, options])
返回值:二者都为 undefined
实例代码:
fs.append('./座右铭.txt', '择其善者而从之,其不善者而改之。', err =>{
if(err) throw err
console.log('追加成功')
})
fs.appendFileSync('./座右铭.txt','\r\n温故而知新,可以为师矣')
\r\n是换行的意思代码示例:

writeFile也可以实现追加写入
但是第二个参数要改flag值

createWriteStream 流式写入
语法:fs.createWriteStream(path[, options])
参数说明:
path文件路径options选项配置(可选)
**返回值: **Object
代码示例:
let ws = fs.createWriteStream("./观书有感.txt");
//写入数据到流
ws.write("半亩方塘一鉴开\r\n");
ws.write("天光云影共徘徊\r\n");
ws.write("问渠那得清如许\r\n");
ws.write("为有源头活水来\r\n");
//关闭写入流,表明已没有数据要被写入可写流
ws.close();程序打开一个文件是需要消耗资源的,流式写入可以减少打开关闭文件的次数。
流式写入方式适用于 大文件写入或者频繁写入的场景,
writeFile适合于 写入频率较低的场景
示例代码:
建立通道,不断开,类似聊天场景

写入文件的场景
文件写入 在计算机中是一个非常常见的操作,下面的场景都用到了文件写入
下载文件
安装软件
保存程序日志,如 Git
编辑器保存文件
视频录制
当 需要持久化保存数据 的时候,应该想到
文件写入
文件读取
文件读取顾名思义,就是通过程序从文件中取出其中的数据,我们可以使用如下几种方式:
| 方法 | 说明 |
|---|---|
| readFile | 异步读取 |
| readFileSync | 同步读取 |
| createReadStream | 流式读取 |
readFile 异步读取
语法: fs.readFile(path[, options], callback)
参数说明:
path 文件路径
options 选项配置
callback 回调函数
返 回 值 : undefined
代码示例:
// 导入 fs 模块
fs.readFile("./座右铭.txt", (error, data) => {
if (err) throw err;
console.log(data);
});
fs.readFile("./座右铭.txt", "uft-8", (error, data) => {
if (err) throw err;
console.log(data);
});
输出结果是buffer,要进行字符串转换
readFileSync 同步读取
语法:fs.readFileSync(path[, options])
参数说明:
path 文件路径
options 选项配置
返 回 值 :string | Buffer
代码示例:
let data = fs.readFileSync('./座右铭.txt')
let data = fs.readFileSync('./座右铭.txt', 'utf-8')
//'utf-8' 指定了文件的编码格式
utf-8(Unicode Transformation Format - 8-bit) 是一种字符编码,它是一种常见的文本编码格式,支持全球多种语言字符,包括中文、英文、符号等。
fs.readFileSync() 默认返回的是 Buffer(缓冲区数据),如果不指定编码格式,数据会以二进制的形式返回。
utf-8 让 fs.readFileSync() 直接返回可读的字符串,而不是 Buffer。createReadStream 流式读取
语法:fs.createReadStream(path[, options])
参数说明:
path 文件路径
options 选项配置(
可选)
返回值:Object
代码示例:
// 创建读取流对象
let rs = fs.createReadStream("./观书有感.txt");
//fs.createReadStream('./观书有感.txt'):创建一个读取流,用于从 观书有感.txt 这个文件中逐步读取数据。
//这个流默认是**分块(chunk)**读取的,而不是一次性读完所有内容,适用于大文件处理。
// 每次取出 64k 数据后执行一次 data 回调
// 绑定一个 data 事件 chunk 块儿 大块儿
rs.on("data", (chunk) => {
console.log(chunk);
console.log(chunk.length); //65536字节,64KB
});
//rs.on('data', callback):当流读取到**一块数据(chunk)**时,触发 data 事件,并传递这块数据给回调函数。
//chunk 是一个 Buffer(缓冲区),存储当前读取到的数据块。
//console.log(chunk.length):输出当前数据块的字节数,这里是 65536,即 64KB(默认的读取流大小)。
// 读取完毕后,执行 end 回调 (可选事件)
rs.on("end", () => {
console.log("读取完毕");
});
//rs.on('end', callback):当整个文件读取完毕时,触发 end 事件。
//这时不会再触发 data 事件,表示文件流已结束。
'data'事件:- 用于处理可读流中实际读取到的数据块。
- 当可读流有数据可读时,会触发
'data'事件,并将数据块作为参数传给事件回调函数。 - 这样你可以在回调函数中对这些数据块进行处理,比如存储、转换或输出等操作。
'end'事件:- 用于知道可读流何时读取完所有数据。
- 当可读流读取完所有数据并到达结尾时,会触发
'end'事件。 - 这个事件告诉你数据读取已经完成,你可以在回调函数中执行最终的数据处理或清理工作。
总的来说:
'data'事件让你可以持续处理从可读流中读取到的数据块。'end'事件则让你知道何时所有数据读取完毕,可以执行最终的操作。
读取文件应用场景
电脑开机
程序运行
编辑器打开文件
查看图片
播放视频
播放音乐
Git 查看日志
上传文件
查看聊天记录
文件复制练习

也可以直接rs.pipe(ws); 建立管道进行迅速复制
流式操作所需要的内存会小很多
文件移动与重命名
在 Node.js 中,我们可以使用
rename或renameSync来移动或重命名文件或文件夹
语法:
fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
参数说明:
oldPath 文件当前的路径
newPath 文件新的路径
callback 操作后的回调
代码示例:
fs.rename("./观书有感.txt", "./论语/观书有感.txt", (err) => {
if (err) throw err;
console.log("移动完成");
});
fs.renameSync("./座右铭.txt", "./论语/.我的座右铭.txt");注: 如果还是移动到当前路径,但是修改了名字,就是重命名了
文件删除
在 Node.js 中,我们可以使用
unlink或unlinkSync来删除文件
语法:
fs.unlink(path, callback)
fs.unlinkSync(path)
参数说明:
path 文件路径
callback 操作后的回调
代码示例:
const fs = require('fs')
fs.unlink('./test.txt', err =>{
if(err) throw err
console.log('删除成功')
})
fs.unlinkSync('./test2.txt')
// 调用 rm 方法 14.4版本引入的新方法 同步 rmSync
fs.rm('./论语.txt', err => {
if (err) {
console.log('删除失败')
return
}
console.log('删除成功')
})
//fs.rm() 删除整个文件夹
//如果要删除一个非空文件夹,需要加上 { recursive: true }:
fs.rm('./myFolder', { recursive: true }, err => {
if (err) {
console.log('删除失败:', err);
return;
}
console.log('文件夹删除成功');
});
✅ 效果:
./myFolder 及其所有子文件、子文件夹都会被删除!
fs.unlink() vs fs.rm()
fs.unlink(path, callback) 仅删除文件 单个文件 ❌
fs.rm(path, callback) 删除文件或文件夹 单个文件 / 目录 ✅(加 { recursive: true })文件夹操作
借助 Node.js 的能力,我们可以对文件夹进行
创建、读取、删除等操作
| 方法 | 说明 |
|---|---|
| mkdir / mkdirSync | 创建文件夹 |
| readdir / readdirSync | 读取文件夹 |
| rmdir / rmdirSync | 删除空文件夹 |
| rm / rmSync | 删除文件或文件夹;递归删除目录时使用 { recursive: true } |
mkdir 创建文件夹
在 Node.js 中,我们可以使用
mkdir或mkdirSync来创建文件夹
语法:
fs.mkdir(path[, options], callback)
fs.mkdirSync(path[, options])
参数说明:
path 文件夹路径
options 选项配置(
可选)callback 操作后的回调
示例代码:
// 异步创建文件夹 mk make 制作 dir directory 文件夹
fs.mkdir("./page", (err) => {
if (err) throw err;
console.log("创建成功");
});
// 递归异步创建 recursive意为递归
fs.mkdir("./1/2/3", { recursive: true }, (err) => {
if (err) throw err;
console.log("递归创建成功");
});
// 递归同步创建文件夹
fs.mkdirSync("./x/y/z", { recursive: true });代码演示:

使文件纵向排列:
进入设置

readdir 读取文件夹
在 Node.js 中,我们可以使用
readdir或readdirSync来读取文件夹
语法:
fs.readdir(path[, options], callback)
fs.readdirSync(path[, options])
参数说明:
path 文件夹路径
options 选项配置(
可选)callback 操作后的回调
示例代码:
// 异步读取
fs.readdir("./论语", (err, data) => {
if (err) throw err;
console.log(data);
});
// 同步读取
let data = fs.readdirSync("./论语");
console.log(data);
rmdir 删除空文件夹、rm 递归删除
rmdir/rmdirSync用于删除空文件夹。递归删除目录应使用rm/rmSync;rmdir的recursive选项已弃用。
语法:
fs.rmdir(path[, options], callback)
fs.rmdirSync(path[, options])
参数说明:
path 文件夹路径
options 选项配置( 可选 )
callback 操作后的回调
示例代码:
// 异步删除文件夹 rm remove 移除
fs.rmdir("./page", (err) => {
if (err) throw err;
console.log("删除成功");
});
// 递归同步删除文件夹
try {
fs.rmSync("./1", { recursive: true });
console.log("递归删除");
} catch (err) {
console.error(err);
}
fs.rmSync("./x", { recursive: true });
// 建议使用
fs.rm("./a", { recursive: true }, (err) => {
if (err) {
console.log(err);
return;
}
console.log("删除成功");
});代码演示:

注:
//异步递归删除文件夹 不推荐:
官方废弃(Deprecation)
Node.js 在 v14.14.0 引入
fs.rm/fs.rmSync;fs.rmdir(path, { recursive: true })后来被弃用,递归删除应使用fs.rm(path, { recursive: true })。当你使用
fs.rmdir(path, { recursive: true })时,会收到如下警告:bash 复制编辑 (node:12345) DeprecationWarning: In future versions of Node.js, fs.rmdir(path, { recursive: true }) will be removed. Use fs.rm(path, { recursive: true }) instead.也就是说,未来的 Node.js 版本可能会直接移除这个方法,代码可能会报错。
查看资源状态
在 Node.js 中,我们可以使用
stat或statSync来查看资源的详细信息
语法:
fs.stat(path[, options], callback)
fs.statSync(path[, options])
参数说明:
path 文件夹路径
options 选项配置( 可选 )
callback 操作后的回调
示例代码:
// 异步获取状态
// stat 方法 status 缩写 状态
fs.stat("/data.txt", (err, data) => {
if (err) throw err;
console.log(data);
});
// 同步获取状态
let data = fs.statSync("./data.txt");
结果值对象结构:
size 文件体积
birthtime 创建时间
mtime 最后修改时间
isFile 检测是否为文件
isDirectory 检测是否为文件夹
....

查看文件类型:

相对路径问题
fs 模块对资源进行操作时,路径的写法有两种:
相对路径
./座右铭.txt当前目录下的 座右铭.txt座右铭.txt等效于上面的写法../座右铭.txt当前目录的上一级目录中的 座右铭.txt
绝对路径
D:/Program Files是 Windows 绝对路径;能否读写取决于目标目录的访问控制,不是整个 C 盘都不能访问/usr/bin是 Linux 绝对路径;普通用户通常没有权限在该系统目录创建文件
相对路径中所谓的
当前目录,指的是命令行的工作目录,而并非是js文件的所在目录所以当命名行的工作目录与文件所在目录不一致时,会出现一些 Bug
解决办法:用绝对路径
注意:
在 Node.js 中,使用相对路径(如
./file.txt)时,所谓的 “当前目录” 并不是指 JS 文件所在的目录,而是命令行的工作目录。如果命令行的工作目录(
process.cwd())与 JS 文件所在的目录不同,文件路径可能会不正确,导致找不到文件或读取失败。1️⃣ 示例:工作目录 vs. 文件所在目录
假设你的项目结构如下:
bash复制编辑/project ├── app.js ├── data │ ├── file.txt在
app.js中,你写了:js复制编辑const fs = require('fs'); fs.readFileSync('./data/file.txt', 'utf-8');这里
./data/file.txt是相对路径。2️⃣ 当命令行的工作目录与 JS 文件所在目录一致
如果你在
project目录下运行:sh复制编辑cd /project node app.js✅ 结果:成功读取
data/file.txt,因为./data/file.txt相对于 /project 目录是正确的。3️⃣ 当命令行的工作目录与 JS 文件所在目录不一致
但如果你在
project/data目录下运行:sh复制编辑cd /project/data node ../app.js❌ 结果:找不到
file.txtperl 复制编辑 Error: ENOENT: no such file or directory, open './data/file.txt'原因:
./data/file.txt的.代表当前工作目录,即/project/data,但file.txt实际在/project/data/file.txt,而不是/project/data/data/file.txt,导致路径错误。
__dirname
在 CommonJS 模块中,__dirname 与 require 看起来可直接使用,但它们由 Node.js 的模块包装器按模块提供,并不是真正挂在 globalThis 上的全局属性;ES 模块中也没有 __dirname。
__dirname 保存着 所在文件所在目录的绝对路径,可以使用 __dirname 与文件名拼接成绝对路径
代码示例:
//=>__dirname + '/data.txt' === 'D:\\Desktop\\Node\\code\\03-fs模块/data.txt'
let data = fs.readFileSync(__dirname + "/data.txt");
console.log(data);使用 fs 模块的时候,尽量使用
__dirname路径转换为绝对路径,这样可以避免相对路径产生的 Bug
练习
编写一个 JS 文件,实现复制文件的功能
/*
* 需求:
* 复制 资料文件夹下的 [笑看风云.mp4]
*/
// 导入 fs 模块
const fs = require("fs");
// 方式一 readFile
// 读取文件内容
let data = fs.readFileSync("./资料/笑看风云.mp4");
// 写入文件
fs.writeFileSync("./资料/笑看风云2.mp4", data);
// 方式二 流式操作
// 创建读取流对象
const rs = fs.createReadStream("./资料/笑看风云.mp4");
// 创建一个写入流对象
const ws = fs.createWriteStream("./资料/笑看风云3.mp4");
// 绑定data事件
// 理想状态下,读取 64k 就写入 64 k,这样消耗的内存最少,实际上读取的速度大于写入的速度
rs.on("data", (chunk) => {
ws.write(chunk);
});
// 绑定data事件 ==== rs.pipe(ws)文件重命名
// 1. 导入 fs 模块
const fs = require("fs");
// 读取 03-fs模块 文件夹
const files = fs.readdirSync("../03-fs模块");
// 遍历数组
files.forEach((item) => {
// 判断
let [num, name] = item.split("-");
if (num < 10) {
num = "0" + num;
}
// 创建新的文件名
let newName = num + "-" + name;
// 重命名
fs.renameSync(`../03-fs模块/${item}`, `../03-fs模块/${newName}`);
});


