在 Node.js 中有两个经常会用到的模块,一个是文件操作模块 fs,一个是文件路径处理模块 path。对于前端程序员来说,无论是做 Node.js 开发还是配置 Webpack 之类的构建工具,都会经常用到这两个模块。

下面是 fspath 模块常用的 API 和使用方法。

path

path 模块是一个核心模块,主要用于处理文件和目录路径。它提供了一组用于解析、拼接、转换和操作文件路径的方法。

normalize 文件路径纠错

normalize 方法的功能是对一些错误的文件路径进行纠错处理,下面是一个错误的文件路径:

const path = require('path');

console.log(path.normalize('/home//user1/1.txt'));
console.log(path.normalize('/root/bt/../'));

处理后的格式为:

\home\user1\1.txt
\root\

因为我用的是 Windows,所以 / 也被转换成 \

join 文件路径拼接

join 的功能是路径拼接:

const path = require('path');

console.log(path.join('C:', 'Program Files', 'Steam', 'steam.exe'));
console.log(path.join('/root/bt/', '1.jpg'));
console.log(path.join(__dirname, '1.jpg'));

拼接后的:

C:\Program Files\Steam\steam.exe
\root\bt\1.jpg
D:\web\nodejs\src\1.jpg

其中 __dirname 可以获取当前运行脚本的所在目录,Windows 和 Linux 获取的格式也会不一样。

resolve 绝对路径转相对路径

resolve 可以根据当前脚本的所在位置把一个相对路径转换为绝对路径,如下:

const path = require('path');

console.log(path.resolve('./'));
console.log(path.resolve('1.jpg'));
console.log(path.resolve('../'));

转换后的路径:

D:\web\nodejs\src
D:\web\nodejs\src\1.jpg
D:\web\nodejs

basename 获取文件名

basename 可以获取一个文件路径中的文件名部分,如下:

const path = require('path');

console.log(path.basename('/root/bt/1.jpg'));
console.log(path.basename('D:\\Program Files\\Steam\\steam.exe'));
console.log(path.basename('/home/user1/'));

获取的文件名:

1.jpg
steam.exe
user1

dirname 获取文件目录

dirname 可以获取一个文件路劲中的文件目录部分:

const path = require('path');

console.log(path.dirname('/home/user1/一首音乐.mp3'));
console.log(path.dirname('D:\\Program Files\\Steam\\steam.exe'));

获取的文件目录:

/home/user1
D:\Program Files\Steam

extname 获取文件后缀

extname 可以获取一个文件名的文件后缀部分:

const path = require('path');

console.log(path.extname('/home/user1/music/音频文件.mp3'));
console.log(path.extname('D:\\下载\\电影\\静水城2021.mkv'));
console.log(path.extname('/home/user1/L.A.Confidential.1997.mp4'));
console.log(path.extname('/home/user1/一个文件'));

获取的后缀:

.mp3
.mkv
.mp4

parse 把文件路径拆分为对象

parse 可以把一个文件路径拆分为包含不同部分的对象:

const path = require('path');

console.log(path.parse('/home/user1/1.jpg'));
console.log(path.parse('D:\\下载\\电影\\静水城2021.mkv'));

拆分后的对象:

{
  "root": "/",
  "dir": "/home/user1",
  "base": "1.jpg",
  "ext": ".jpg",
  "name": "1"
}
{
  "root": "D:\\",
  "dir": "D:\\下载\\电影",
  "base": "静水城2021.mkv",
  "ext": ".mkv",
  "name": "静水城2021"
}

还有一个 format 功能和 parse 相反,可以把一个文件路径对象转换为 String 的完整文件路径。

fs

fs 模块是一个核心模块,提供了对文件系统的访问和操作功能。它允许你读取、写入、修改、删除文件等。

fs 的很多 API 都会提供两个方法,一个是异步执行的,一个是同步执行。异步就是在执行的过程中不会影响后面代码的执行,执行完成后需要通过回调函数来获取执行结果。同步就是在执行的过程中,后面的代码也会暂停,只有执行完成后,后面的代码才能继续执行,同步执行完成后会直接返回执行结果,不需要通过回调函数来获取。

readFile 和 readFileSync 读取文件

readFilereadFileSync 都可以读取文件。readFile 是异步读取文件,读取文件的过程中不会影响后面代码的执行,读取文件后需要通过回调函数来获取文件内容。readFileSync 是同步读取文件,读取过程中后面的代码也会暂停执行,读取完成后直接返回文件内容。

readFile 可以接收三个参数,第一个参数是文件名,第二个参数可以是文件编码或回调函数,第三个参数是回调函数,读取完成后会返回一个 Buffer

下面使用 readFile 读取一个文本:

const fs = require('fs');

fs.readFile('文本.txt', (err, data) => {
  // 如果出错就输出错误信息
  if (err) throw err;
  // 把读取的内容转换为文本在控制台输出
  console.log(data.toString());
});

在读取文本内容的时候,如果第二个参数传入了字符编码,读取完成后就直接是 String 内容:

fs.readFile('文本.txt', 'utf-8', (err, data) => {
  // 如果出错就输出错误信息
  if (err) throw err;
  // 直接在控制台输出读取的文本
  console.log(data);
});

readFileSyncreadFile 是差不多的,第一个参数是文件名,第二个参数是字符编码,读取完成后直接返回文件内容:

const fs = require('fs');

const data = fs.readFileSync('文本.txt', 'utf-8');
// 直接在控制台输出读取的文本内容
console.log(data);

如果省略字符编码的话,读取完成后会返回 Buffer

writeFile 和 writeFileSync 写入文件

writeFile 可以异步写入文件,writeFileSync 可以同步写入文件。

writeFile 的第一个参数是要写入的文件名,第二个参数是要写入的内容,第三个参数是字符编码,第四个参数是执行完成的回调函数。

下面把一段字符串写入到 文本.txt

const fs = require('fs');

const text = '我的 Github https://github.com/changbin1997';
fs.writeFile('文本.txt', text, 'utf-8', err => {
  if (err) throw err;
  console.log('success');
});

writeFileSyncwriteFile 的参数是差不多的,只是 writeFileSync 少一个回调函数,writeFileSync 也不会返回执行结果,要获取 writeFileSync 的执行结果可以使用 try

stat 和 statSync 获取文件信息

stat 是异步获取文件信息,statSync 是同步获取文件信息。

stat 的第一个参数是文件名,第二个参数是执行完成的回调函数,回调函数可以接收 error 错误和 stats 文件信息。

下面使用 stat 获取文件信息:

const fs = require('fs');

fs.stat('文本.txt', (err, stats) => {
  if (err) throw err;
  console.log(stats);
});

文件信息是一个对象,内容如下:

{
  "dev": 250892065,
  "mode": 33206,
  "nlink": 1,
  "uid": 0,
  "gid": 0,
  "rdev": 0,
  "blksize": 4096,
  "ino": 8725724278044770,
  "size": 45,
  "blocks": 0,
  "atimeMs": 1694350024660.436,
  "mtimeMs": 1694350021596.3835,
  "ctimeMs": 1694350021596.3835,
  "birthtimeMs": 1694342831693.027,
  "atime": "2023-09-10T12:47:04.660Z",
  "mtime": "2023-09-10T12:47:01.596Z",
  "ctime": "2023-09-10T12:47:01.596Z",
  "birthtime": "2023-09-10T10:47:11.693Z"
}

下面是文件信息说明:

  • dev:表示设备的数字标识符。
  • mode:表示文件的权限和类型。可以通过stat.mode.toString(8)将其转换为八进制字符串形式。
  • nlink:表示文件的硬链接数量。
  • uid:表示文件所有者的数字用户标识符。
  • gid:表示文件所属组的数字组标识符。
  • rdev:仅在文件是特殊文件时有意义,表示设备的数字标识符。
  • ino:表示文件的inode编号。
  • size:表示文件的大小(以字节为单位)。
  • blksize:表示文件系统块的大小。
  • blocks:表示分配给文件的块数。
  • atimeMs:表示文件的最近一次访问时间(毫秒级时间戳)。
  • mtimeMs:表示文件的最近一次修改时间(毫秒级时间戳)。
  • ctimeMs:表示文件的最近一次更改时间(毫秒级时间戳)。
  • birthtimeMs:表示文件的创建时间(毫秒级时间戳)。
  • atime:表示文件的最近一次访问时间(Date对象)。
  • mtime:表示文件的最近一次修改时间(Date对象)。
  • ctime:表示文件的最近一次更改时间(Date对象)。
  • birthtime:表示文件的创建时间(Date对象)。

文件信息还包含一个 isFile() 方法,可以用来判断是文件还是目录,如果 isFile()true 就是文件。

statSync 可以同步获取文件信息,传入一个文件名,返回文件信息。

rename 和 renameSync 重命名

rename 的第一个参数是要重命名的文件名,第二个参数是新文件名,第三个参数是执行完成的回调函数,回调函数可以接收 error 错误信息。

下面把一个 文本.txt 重命名为 text.txt

const fs = require('fs');

fs.rename('文本.txt', 'text.txt', err => {
  if (err) throw err;
  console.log('success');
});

renameSync 的参数和 rename 是一样的,只是 renameSync 没有回调函数。

unlink 和 unlinkSync 删除

unlink 的第一个参数是要删除的文件名,第二个参数是执行完成的回调函数,回调函数可以接收 error 错误信息。

下面使用 unlink 删除文件:

const fs = require('fs');

fs.unlink('text.txt', err => {
  if (err) throw err;
  console.log('已删除');
});

unlinkSync 只有一个参数,要删除的文件名。

readdir 和 readdirSync 获取指定目录下所有文件名

readdir 的第一个参数是目录路径,第二个参数是执行完成的回调函数,回调函数可以接收 error 错误信息和 files 文件名。

下面使用 readdir 获取上一层目录下所有文件的名称:

const fs = require('fs');

fs.readdir('../', (err, files) => {
  if (err) throw err;
  console.log(files);
});

获取的文件名是一个数组:

[".idea", "node_modules", "package-lock.json", "package.json", "src"]

readdirSync 需要传入一个目录路径,返回文件名数组。

mkdir 和 mkdirSync 创建目录

mkdir 的第一个参数是要创建的目录名称,第二个参数是执行完成的回调函数,回调函数可以接收 error 错误信息。

mkdirSync 只有一个参数,要创建的目录名称。

rmdir 和 rmdirSync 删除目录

rmdir 的第一个参数是要删除的目录名称,第二个参数是执行完成的回调函数,回调函数可以接收 error 错误信息。

注意,rmdir 只能删除空目录,不能删除包含文件的目录!如果需要删除目录需要先使用 unlink 删除所有文件后才能删除目录。

watch 监听文件或目录变化

watch 可以监听一个文件或目录变化,如果文件或目录发生改变可以通过回调函数获取相关信息。

watch 的第一个参数是要监听的文件或目录名称,第二个参数是选项,第三个参数是发生变化时的回调函数。选项需要传入一个对象,对象包含 recursive 是否监听子目录,可以设置为 truefalse。回调函数可以接收 event 变化类型和 filename 文件名。

下面使用 watch 监听当前脚本所在目录,我会删除一个 新建文本文档.txt

const fs = require('fs');

fs.watch(__dirname, {
  recursive: true
}, (event, filename) => {
  console.log(event);
  console.log(filename);
});

回调函数的 eventrenamefilename新建文本文档.txt

fs 还有个 watchFile 方法,可以用来监听单个文件。