fs文件系统模块
-
- 1、文件的读取与写入
-
- 1.1、检查文件是否存在
- 1.2、文件读取
- 1.3、文件写入
- 1.4、文件操作时的异常处理
-
- [1.4.1、 同步操作的异常处理](#1.4.1、 同步操作的异常处理)
- [1.4.2、 异步操作的异常处理](#1.4.2、 异步操作的异常处理)
- 2、文件操作
- 3、目录操作
1、文件的读取与写入
fs是file system的简写,表示文件系统,在Node.js中使用fs模块之前,首先需要使用require()方法将fs模块引入,代码如下:
js
const fs=require('fs');
1.1、检查文件是否存在
fs模块内置许多方法,用以对文件进行相关操作。具体使用时,有的方法如果发现文件不存在,可以创建文件,而有的方法则不能,这时就会出现错误,为了避免这类错误,在对文件进行操作之前,一般都需要检测文件是否存在,并且根据需要检查文件的可读或可写等属性。检查文件是否存在及其属性可以通过access()方法实现,语法格式如下:
js
fs.access(path,mode, callback)
path:文件的路径。mode:要执行的可访问性检查,默认值为fs.constants.F_OK。F_OK:指示文件对调用进程可见的标志。这对于确定文件是否存在很有用,但没有说明rwx权限R_OK:指示文件可以被调用进程读取的标志W_OK:指示文件可以被调用进程写入的标志X_OK:指示文件可以被调用进程执行的标志,在Windows系统中等效于fs.constants.F_OK
callback:回调函数,使用一个可能的错误参数进行调用。如果检查可访问性失败,则错误参数将是Error对象
常见的Error对象值如表所示:
| 值 | 说明 | 值 | 说明 |
|---|---|---|---|
| EPERM | 操作不允许 | EACCES | 拒绝访问 |
| ENOENT | 文件或者路径不存在 | EFAULT | 地址错误 |
| ESRCH | 进程不存在 | EEXIST | 文件已经存在 |
| EINTR | 系统调用中断 | ENODEV | 设备不存在 |
| EIO | I/O错误 | ENOTDIR | 路径不存在 |
| ENXIO | 设备或地址不存在 | EISDIR | 是一个路径 |
| EBIG | 参数列表过长 | EINVAL | 参数无效 |
| ENOEXEC | 执行格式错误 | ENFILE | 文件表溢出 |
| EBADF | 文件编号错误 | EMFILE | 打开的文件过多 |
| ECHILD | 子进程不存在 | EFBIG | 文件太大 |
| EAGAIN | 重试 | ENOSPC | 剩余空间不足 |
| ENOMEM | 内存不足 | EROFS | 只读文件系统 |
| EBUSY | 资源繁忙或者被锁定 | ENOTEMPTY | 非空目录 |
fs模块提供对文件与目录进行操作的方法时,通常分别提供了同步方法和异步方法,其中,同步方法名通常是在异步方法名后面加了Sync后缀,如本节中所讲的access()方法的对应同步方法为accessSync(),但除了文件读写操作,一般都默认使用异步方法。
例如,查看demo.txt和demo1文件是否存在,示例代码如下:
js
const fs = require("fs")
//查看demo.txt文件是否存在
fs.access("demo.txt",fs.constants.F_OK, (err)=>{
if (err) {
console.log("demo.txt文件不存在");
} else {
console.log("demo.txt文件存在");
}
});
//查看demo1文件是否存在
fs.access("demo1 ",fs.constants.F_OK, (err)=>{
if (err) {
console.log("demo1文件不存在");
} else {
console.log("demo1文件存在");
}
});
demo.txt文件存在
demo1文件不存在
上面示例检查文件是否存在,除此之外,还可以检查文件的相关属性。如要检查文件是否可读,可以将上面代码中第3行的fs.constants.F_OK修改为fs.constants.R_OK;而如果要检查文件是否可写,则可以将fs.constants.F_OK修改为fs.constants.W_OK。另外,access()方法的mode参数也可以同时设置多个值,如果mode参数有多个值,中间用|分割。例如,检查demo.txt文件是否存在且是否可写的代码如下:
js
const fs = require("fs")
//查看demo.txt文件是否存在且可写
fs.access("demo.txt",fs.constants.F_OK | fs.constants.W_OK, (err)=>{
if (err) {
console.log(err)
if(err.code=="ENOENT"){
console.log("demo.txt文件不存在");
} else if(err.code="EPERM"){
console.log("demo.txt文件存在,但不可写")
} else{
console.log("未知错误")
}
} else {
console.log("demo.txt存在,并且可写");
}
});
在项目文件夹中放置一个设置为只读的demo.txt文件,然后运行上面代码,可看到运行结果如下:
demo.txt存在,并且可写
access()方法不仅可以检测文件是否存在,也可以检测文件夹是否存在。
1.2、文件读取
fs模块为读取文件提供了两个方法,即readFile()方法和readFileSync()方法,二者的区别是,前者为异步读取文件(默认操作),后者为同步读取文件,这两个方法的语法格式如下:
js
fs.readFile(file,encoding,callback)
fs.readFileSync(file,encoding)
file:文件名。encoding:文件的编码格式。callback:回调函数。
例如,下面代码使用fs模块的readFileSync()方法和readFile()方法分别对文件poems.txt和demo.txt进行同步和异步读取,并显示读取的内容,代码如下:
js
const fs = require('fs');
//使用readFileSync()方法同步读取文件
let text = fs.readFileSync('poems.txt', 'utf-8');
console.log(text);
//使用readFile()方法异步读取文件
fs.readFile('demo.txt', 'utf-8', (error, data)=>{
console.log(data);
});
同步方法读取的文本
异步方法读取的文本
fs模块中的大部分功能都可以通过同步方法和异步方法来实现,这二者的区分方法是,方法名中含有Sync后缀的是同步方法。同步方法和异步方法的区别是:同步方法立即返回操作结果,在使用同步方法执行的操作结束之前,不能执行后续代码;异步方法将操作结果作为回调函数的参数进行返回,在方法调用之后,可以立即执行后续代码。在大多数情况下,应该调用异步方法,但是在很少的场景中,比如读取配置文件启动服务器的操作中,应该使用同步方法。
模拟听歌时的显示歌词效果:
在该项目文件夹中添加一个歌词文件song.txt,然后创建js.js文件,在js.js文件中读取歌词文件中的内容,读取时,需要根据音乐播放进度显示对应的歌词,这需要使用正则表达式对歌词文件中的时间点进行解析(解析的格式为[00:00.00]),以显示对应时间点的歌词内容。代码如下:
js
//引入模块
const { timeLog } = require('console');
const fs = require('fs');
//读取歌词文件
fs.readFile('./song.txt', (err, data)=>{
if (err) {
return console.log('歌词文件读取失败');
}
data = data.toString();
let lines = data.split('\n');
//遍历所有行,通过正则表达式匹配对应时间点(时间点的格式为[00:00.00]),并输出对应的歌词
let reg = /\[(\d{2})\:(\d{2})\:(\d{2})\]\s*(.+)/;
for (let i = 0; i < lines.length; i++) {
(function(index) {
let line = lines[index];
let matches = reg.exec(line);
if (matches) {
//获取分
let m = parseFloat(matches[1]);
//获取秒
let s = parseFloat(matches[2]);
//获取毫秒
let ms = parseFloat(matches[3]);
//获取定时器中要输出的内容
let content = matches[4];
//将分+秒+毫秒转换为毫秒
let time = m * 60 * 1000 + s * 1000 + ms;
//使用定时器,让每行内容在指定的时间输出
setTimeout(function() {
console.log(content);
}, time);
}
})(i);
}
});
[ti:hello]
[ar:张鑫]
[t_time:(03:46)]
[00:00:00] hello - 张鑫
[00:02:00] 词:王人
[00:04:00] 曲:王人
[00:06:00] 编曲:梁桦
[00:08:00] 歌词编辑:东东
[00:18:23] 每件事情都有发生的理由
[00:21:72] 可无法解释 遇见你
[00:26:15] 再多的心里准备 都抵抗不了
[00:29:89] 被显示打败的爱情
[00:24:27] 我们都习惯了同一个温度
本实例的JavaScript代码中,for循环中所有的内容都放在了匿名函数中,并且该匿名函数需要自动执行,这样在执行该文件时,保证了每次循环都会输出一句歌词。
1.3、文件写入
文件写入时,有4个方法供选择,分别为writeFile()方法、writeFileSync()方法、appendFile()方法和appendFileSync()方法,下面分别进行介绍。
1.3.1、writeFile()和writeFileSync()
这两个方法分别用来对文件进行异步和同步写入,它们的语法格式如下:
js
fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])
file:文件名或文件描述符。data:写入文件的内容,可以是字符串也可以是缓冲区。options:可选参数,可以为以下内容。- encoding:编码方式,默认值为utf8,如果data为缓冲区,则忽略encoding参数。
- mode:文件的模式。默认值为0o666。
- flag:文件系统标志。默认值为w。
- signal:允许中止正在进行的写入文件操作。
callback:回调函数。
创建文件并且向文件中写入内容
js
const fs = require('fs');
//声明要写入的内容
const data = '\t春夜喜雨\n\t 杜甫\n好雨知时节,当春乃发生。\n随风潜入夜,润物细无声。'
+ '\n野径云俱黑,江船火独明。\n晓看红湿处,花重锦官城。';
//使用异步方式向poems.txt文件中写入古诗
fs.writeFile('poems.txt', data, 'utf8', (error)=>{
if (error) {
throw error;
}
console.log('异步写入文件完成');
});
//使用同步方式向一个本不存在的newpeoms.txt文件中同样写入古诗
fs.writeFileSync('newpoems.txt', data, 'utf8');
console.log('同步写入文件完成!');
运行程序,进入项目文件夹中,可以发现新增了newpoems.txt文件,这说明使用writeFileSync方法向文件中写入内容时,如果文件不存在,系统会自动创建。分别打开poems.txt文件和newpeoms.txt文件,发现它们的内容都是古诗《春夜喜雨》,这说明使用writeFile方法向文件中写入内容时,会覆盖掉原有内容。
1.3.2、appendFile()和appendFileSync()
这两个方法分别向文件异步追加内容和同步追加内容,它们的语法格式如下:
js
fs.appendFile(path, data[, options],callback)
fs.appendFileSync(path, data[, options])
path:文件路径。data:要写入文件的数据。data:要写入文件的数据。options:可选参数,可以为以下内容。- encoding:编码方式,默认值为utf8。
- mode:文件模式,默认值为0o666。
- flag:文件系统标志,默认值为a。
为古诗增加古诗鉴赏内容。
js
const fs = require("fs")
const path = "poems.txt"
const data = "\n古诗鉴赏:这首诗描写细腻、动人。诗的情节从概括的叙述到形象的描绘,由耳闻到目睹,当晚到次晨,结构谨"
+ "严。用词讲究。颇为难写的夜雨景色,却写得十分耀眼突出,使人从字里行间呼吸到一股令人喜悦的春天气息。"
fs.appendFile(path, data, (err)=>{
if (err) {
console.log(err)
} else {
console.log("内容追加完成")
}
})
1.4、文件操作时的异常处理
1.4.1、 同步操作的异常处理
使用同步方法进行文件操作时,可以使用try-catch语句进行异常处理。示例代码如下:
js
const fs = require('fs');
//文件读取
try {
const data = fs.readFileSync('textfile.txt', 'utf8');
console.log(data);
} catch (e) {
console.log(e);
}
//文件写入
try {
fs.writeFileSync('textfile.txt', 'Hello World .. !', 'utf8');
console.log('完成文件写入操作');
} catch (e) {
console.log(e);
}
1.4.2、 异步操作的异常处理
使用异步方法进行文件操作时,可以使用if-else语句进行异常处理。示例代码如下:
js
const fs = require('fs');
//文件读取
fs.readFile('textfile.txt', 'utf8', (error, data)=>{
if (error) {
console.log(error);
} else {
console.log(data);
}
});
//文件写入
fs.writeFile('textfile.txt', 'Hello World .. !', 'utf8', (error)=>{
if (error) {
console.log(error);
} else {
console.log('完成文件写入操作');
}
});
2、文件操作
2.1、截断文件
在fs模块中,可以使用truncate()方法对文件进行截断操作,所谓截断,是指删除文件内的一部分内容,以改变文件的大小。其语法格式如下:
js
fs.truncate(path[, len], callback)
path:用于指定要被截断文件的完整文件路径及文件名。len:一个整数数值,用于指定被截断后的文件大小(以字节为单位)。callback:用于指定截断文件操作完毕时执行的回调函数,该回调函数中使用一个参数,参数值为截断文件操作失败时触发的错误对象。
当len为0时,说明文件的内容为空。
js
const fs = require('fs');
fs.stat("poems.txt", (err,stats)=>{
console.log("原文件大小为:"+stats.size+"字节")
})
fs.truncate('poems.txt', 140, (err)=>{
if (err)
console.log('对文件进行截断操作失败。');
else {
fs.stat('poems.txt', (err, stats)=>{
console.log('截断操作已完成\n文件大小为:' + stats.size + '字节。');
});
}
});
原文件大小为:466字节
截断操作已完成
文件大小为:140字节。
2.2、删除文件
在fs模块中,可以使用unlink()方法对文件进行删除操作,其语法格式如下:
js
fs.unlink(path, callback)
path:用于指定被删除文件的路径。callback:回调函数。
js
const fs = require("fs");
console.log("准备删除文件!");
fs.unlink('poems.txt', (err)=>{
if (err) {
console.error(err);
}
console.log("文件删除成功!");
});
2.3、复制文件
文件复制有两种形式:一种是将文件从一个位置复制到另外一个位置,另一种则是从原文件中读取数据并写入一个新文件中。针对这两种形式的文件复制操作,fs模块都提供了相应的方法来实现。另外,由于复制文件时可能会有同步操作和异步操作。
2.3.1、使用copyFile()与copyFileSync()复制文件
copyFile()方法与copyFileSync()方法分别用于异步和同步复制文件,它们用来将文件从一个位置复制到另外一个位置,其语法格式如下:
src:要复制的源文件名。dest:要复制的目标文件名。mode:复制操作的修饰符,默认值为0。callback:回调函数。
js
const fs = require("fs")
fs.copyFile("poems.txt", "poems1.txt", (err)=>{ //异步复制文件
if (err) {
console.log("复制文件失败") //提示复制失败
console.log(err) //显示错误信息
} else {
console.log("复制文件成功") //提示复制成功
}
})
2.3.2、使用readFile()和writeFile()复制文件
除了上面介绍的直接复制文件的方式,还可以通过复制文件内容的方式实现文件的复制,这需要使用fs模块中的readFile()方法和writeFile()方法。其中,readFile()方法用来读取要复制的文件内容,writeFile()方法用来将读取到的文件内容写入新建的文件中,从而实现复制文件的功能。
js
const fs = require("fs")
//读取文件
fs.readFile("poems.txt", "utf8", (err, data)=>{
if (err) {
console.log("读取文件失败")
console.log(err)
} else {
console.log("读取文件成功")
//写入文件
fs.writeFile("poems1.txt", data, (err)=>{
if (err) {
console.log("写入文件失败")
console.log(err)
} else {
console.log("写入文件成功")
}
})
}
})
2.4、 重命名文件
在fs模块中,可以使用rename()方法为文件重命名,重命名文件的同时会更改文件的路径,其语法格式如下:
js
fs.rename(oldPath, newPath, callback)
oldPath:原文件名(目录名)。newPath:新文件名(目录名)。callback:回调函数。
js
const fs=require("fs")
fs.rename("poems.txt", "春夜喜雨.txt", (err)=>{
if(err){
console.log("糟糕!重命名文件失败")
console.log(err)
} else{
console.log("重命名文件成功")
}
})
批量重命名文件。
js
const fs = require("fs")
//读取demo文件夹中的文件名
fs.readdir("demo", (err,files) => {
for (let i = 0; i < files.length; i++) { //遍历文件夹中的文件
let fl = "demo/" + files[i] //记录要重命名的文件名(包括路径)
//读取文件,并将文件中的内容以换行符进行分割存储到数组中
let data = fs.readFileSync(fl, "utf8").split("\n")
//获取文件中第一行内容并去掉所有空白字符(包括空格和换行符等)
let title = data[0].replace(/\s*/g, '')
fs.rename(fl, "demo\\" + title + ".txt", (err)=>{ // 重命名文件
if(err)
console.error(err.message)
})
}
})
3、目录操作
3.1、创建目录
创建目录可以使用mkdir()和mkdirSync()方法实现,它们分别用来异步和同步创建目录,语法格式如下:
js
fs.mkdir(path[, options], callback)
fs.mkdirSync(path[, options])
path:要被创建的目录的完整路径及目录名。options:指定目录的权限,默认为0777(表示任何人可读可写该目录)。callback:指定创建目录操作完毕时调用的回调函数,该回调函数中只有一个参数,参数值为创建目录操作失败时触发的错误对象。
批量创建文件并放到指定的文件夹中。
js
const fs = require("fs")
//检查demo文件夹是否存在
fs.access("demo", fs.constants.F_OK, (err)=>{
if (err) { //如果不存在,则创建demo文件夹,反之则继续下一步
fs.mkdir("demo", (err)=>{
if(err)
console.log("糟糕,创建文件夹时出错了")
})
}
//读取b.txt文件,该文件中保存了要批量创建的文件的名称
let data = fs.readFileSync("b.txt", "utf8").split("\n")
//逐个创建文件
for (let i = 0; i < data.length; i++) {
let title = data[i].replace("\n", "") //去掉读取到的内容中的换行符
fs.writeFile("demo/" + title + ".txt", "", (err)=>{
if(err)
console.log("创建文件失败")
})
}
})
这里需要注意的是,创建目录时需要一级一级地创建,而不是直接创建多级目录。例如,要创建目录"第1章\第1节",首先需要保证"第1章"目录存在,然后才能创建目录"第1节",例如,下面的代码是错误的。
3.2、读取目录
在fs模块中,可以使用readdir()方法或者readdirSync()方法读取目录,其中,readdir()方法用来异步读取目录,readdirSync()方法用来同步读取目录。由于在实际应用中,通常都使用默认的readdir()方法异步读取,因此这里主要讲解readdir()方法的使用,其语法格式如下:
js
fs.readdir(path[, options], callback)
path:文件名或者文件描述符。options:可选参数,可以为如下值。- encoding:编码方式。
- flag:文件系统标志。
- signal:允许中止正在进行的读取文件操作。
callback:回调函数,在回调函数中有两个参数,具体如下。err:出现错误时的错误信息。data:调用成功时的返回值。
js
const fs = require("fs")
fs.readdir('/', (err, files)=>{
if (err)
console.log('读取文件夹操作失败。');
else
console.log(files);
});
3.3、删除空目录
在fs模块中,可以使用rmdir()方法或者rmdirSync()方法删除空目录,其中,rmdir()方法用来异步删除空目录,rmdirSync()方法用来同步删除空目录。由于在实际应用中,通常都使用默认的rmdir()方法进行异步删除,因此这里主要讲解rmdir()方法的使用,语法格式如下:
js
fs.rmdir(path[, options], callback)
path:用于指定要被删除目录的完整路径以及目录名。options:可选参数,可以为如下值。- recursive:如果为true,则执行递归目录删除操作。在递归模式下,操作将在失败时重试。默认值为false。
- retryDelay:重试之间等待的时间(以毫秒为单位)。如果recursive选项不为true,则忽略此选项。默认值为100。
- maxRetries:表示重试次数,如果遇到EBUSY、EMFILE、ENFILE、ENOTEMPTY或EPERM错误,Node.js将在每次尝试时,以retryDelay毫秒的线性退避等待时间重试该操作。如果recursive选项不为true,则忽略此选项。默认值为0。
callback:用于指定删除目录操作完毕时调用的回调函数。
js
const fs = require("fs")
fs.rmdir('./demo', (err)=>{
if (err){
console.log('删除空目录操作失败。');
console.log(err)
} else
console.log('删除空目录操作成功。');
});
3.4、查看目录信息
在fs模块中,可以使用stat()方法或者lstat()方法查看目录或文件信息,但如果是查看链接文件的信息,就必须使用lstat()方法。stat()方法或者lstat()方法的语法格式如下:
js
stat(path,callback)
lstat(path,callback)
path参数用于指定要被查看的目录或文件的完整路径(包括目录名或者文件名callback参数用于指定查看目录或文件操作完毕时调用的回调函数,该回调函数中包括两个参数,即err和stats。err表示出现错误时的错误信息,stats为一个对象,表示文件的相关属性。
js
const fs = require("fs")
fs.stat("demo", (err, stats)=>{
if(err){
console.log("获取文件夹信息失败")
} else{
console.log(stats)
}
})
json
Stats {
dev: 16777223,
mode: 16877,
nlink: 6,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 59949263,
size: 192,
blocks: 0,
atimeMs: 1774968474330.311,
mtimeMs: 1774968123349.6536,
ctimeMs: 1774968123349.6536,
birthtimeMs: 1774967662904.29,
atime: 2026-03-31T14:47:54.330Z,
mtime: 2026-03-31T14:42:03.350Z,
ctime: 2026-03-31T14:42:03.350Z,
birthtime: 2026-03-31T14:34:22.904Z
}
目录的属性信息如下标:
| 属性 | 说明 |
|---|---|
dev |
表示文件或目录所在的设备D |
mode |
表示文件或目录的权限 |
nlink |
表示文件或目录的硬连接数量 |
uid |
表示文件或目录的所有者的用户ID |
gid |
表示文件或目录所属组的数字标识 |
rdev |
表示字符设备文件或块设备文件所在的设备ID |
blksize |
表示文件或目录中I/O操作的块大小(以字节为单位) |
ino |
表示文件或目录的索引编号 |
size |
表示文件或目录的大小(即文件中的字节数) |
blocks |
表示分配给文件或目录的块数 |
atimeMs |
表示最后一次访问文件或目录时的时间戳(以毫秒为单位) |
mtimeMs |
表示最后一次修改文件或目录时的时间戳(以毫秒为单位) |
ctimeMS |
表示最后一次更改文件或目录状态时的时间戳(以毫秒为单位) |
birthtimeMs |
表示创建文件或目录时的时间戳(以毫秒为单位) |
atime |
表示上次访问文件或目录的时间戳 |
mtime |
表示上次修改文件或目录时的时间戳 |
ctime |
表示上次更改文件或目录状态时的时间戳 |
birthtime |
表示文件或目录创建时的时间戳 |
stat对象的相关方法:
| 方法 | 说明 |
|---|---|
isFile() |
判断是否是文件 |
isDirectory() |
判断是否是目录(文件夹) |
isBlockDevice() |
判断是否是块设备 |
isCharacterDevice() |
判断是否是字符设备 |
isFIFO() |
判断是否是FIFO存储器 |
isSocket() |
判断是否是socket协议 |
js
const fs = require("fs")
fs.stat("b.txt", (err, stats)=>{
console.log("是否为文件", stats.isFile())
console.log("是否为文件夹/目录", stats.isDirectory())
})
3.5、获取目录的绝对路径
在fs模块中,可以使用realpath()方法或realpathSync()方法获取指定目录的绝对路径,其中,realpath()方法用于异步操作,realpathSync()方法用于同步操作。由于在实际应用中,通常都使用默认的realpath()方法进行异步操作,因此这里主要讲解realpath()方法的使用,其语法格式如下:
js
fs.realpath(path[, options], callback)
path:路径,可以为字符串或者url。options:一般为encoding,用于指定编码格式,默认为utf8。callback:回调函数,该回调函数中有两个参数,具体如下。- err:发生错误时的错误信息。
- resolvedPath:绝对路径。
js
const fs = require("fs")
fs.realpath('b.txt', (err,resolvedPath)=>{
if (err){
console.log('获取绝对路径失败');
console.log(err)
}else
console.log(resolvedPath);
});