Node.js 内置模块之 fs

fs(File system,文件系统) 是 node 的一个内置模块,可用于在多种操作系统中(Windows 、macOS 等)对文件进行操作。查阅 node 的官方文档,可以看到 File system 下有非常多的 API:

它们有的用于对文件进行读写操作,有的用于对文件夹操作等,且都提供同步异步Promise 这 3 种操作方式。下面举几个常用的 API 为例,进行说明。

操作文件

读取文件

同步读取

fs.readFileSync() 用于同步 (Sync) 读取文件,直接传入要读取的文件的路径或文件描述符即可:

// 代码块 1.1.1
const fs = require('fs')
// 同步读取
const resSync = fs.readFileSync('./test.txt')
console.log(resSync)
console.log('看看会不会阻塞') 

执行结果如下,第 6 行的打印是在第 5 行之后,说明同步操作会阻塞后续代码的运行:

resSync 的结果是 Buffer 对象,本质上是二进制的内容,只是以十六进制来展示。如果想看到文本内容,可以对结果使用 toString() 进行转换:console.log(resSync.toString()),或者给 fs.readFileSync() 传入第 2 个参数 —— 一个用于指定配置的可选对象,该对象有 2 个属性:

  • encoding,默认值为 null ,也就是结果显示为 buffer,想显示文本可以改为 'utf-8' (或 'utf8');
  • flag,读取文件时默认值为 'r',更多 flag 相关信息可查看官方文档
// 代码块 1.1.2
const resSync = fs.readFileSync('./test.txt', {encoding: 'utf-8',flag: 'r'
})
console.log(resSync)
console.log('看看会不会阻塞') 

现在看到的 resSync 就为一个字符串了:

异步读取 - Callback

fs.readFile() 用于异步读取文件,其可以传入 3 个参数,前两个与 fs.readFileSync() 相同,第 3 个参数为一个回调函数,当获取到读取结果时被调用:

// 代码块 1.2.1
fs.readFile('./test.txt',{encoding: 'utf-8'},(err, data) => {if (!err) console.log(data)}
)
console.log('看看会不会阻塞') 

执行结果如下,可以看到异步读取不会阻塞后续代码的执行:

异步读取 - Promise

fs.promises.readFile() 也是用于异步读取文件,但可以避免在采用回调函数的方式获取结果时容易产生的回调地狱:

// 代码块 1.3.1
fs.promises.readFile('./test.txt', {encoding: 'utf-8'}).then(res => {console.log(res)}).catch(err => console.log(err)) 

写入文件

写入文件也有同步异步的方法,下面以异步 - callback 的 api fs.writeFile()为例。其第 1 个参数可以是文件的地址或文件描述符,如果文件不存在,则会进行创建;第 2 个参数为要写入的内容;第 3 个参数为用于指定配置的可选对象,其中有之前介绍过的两个属性 encodingflag,只不过这里 encoding 的默认值为 'utf8'flag 的默认值为 'w',即默认写入的内容会覆盖原有的内容,如果是想在原内容后追加写入,则可以改为 'a' (append);第 4 个参数是一个回调函数,在写入操作执行结束后调用,如果写入发生错误,就会把错误信息作为参数传入:

fs.writeFile('./test.txt','Hello Juejin',{encoding: 'utf8',flag: 'w'},err => {if (err) {console.log(err)return}console.log('写入成功')}
) 

文件描述符

前面说到,读取文件的这 3 种方法,传入的第一个参数除了文件的路径外,还可以是文件描述符(file descriptor)。在常见的操作系统中,内核对于每个进程都维护着一张当前打开的文件和资源的表格,而每个打开的文件都会被分配一个简单的数字用于标识和跟踪文件,虽然不同的操作系统具体实现可能不同,但 node 帮我们处理了差异,为每个打开的文件分配了个数字类型的文件描述符。

我们可以通过 fs.open() 打开一个文件来获取文件描述符:

// 代码块 2.1.1
fs.open('./test.txt', (err, fd) => {if (!err) {console.log(fd)}
}) 

查看类型可以得知其第 2 个参数,也就是回调函数会被传入两个参数,第 1 个为错误信息 err,第 2 个就是文件描述符 fd

得到的结果为 3:

查看文件信息

获取到了文件描述符,我们可以通过 fs.fstat() (其第 1 个参数只能是 fd)查看文件信息:

fs.open('./test.txt', (err, fd) => {if (err) returnfs.fstat(fd, (err, stats) => {if (err) returnconsole.log(stats)fs.close(fd) // 关闭文件})
}) 

打印结果如下:

操作文件夹

创建文件夹

fs 模块还能对文件夹进行操作,比如 fs.mkdir() 就可以创建文件夹,mkdir 可以看成是 make directory 的缩写:

fs.mkdir('./test', err => console.log(err)) // 创建 test 文件夹 

读取文件夹

fs.readdir() 可以用于读取文件夹,在回调函数中会返回读取到的文件夹中所包含的文件或文件夹,比如 node 目录下的结构如下图所示:

执行如下代码对 node 目录进行读取:

// fs.js
fs.readdir('../node', (err, files) => {if (!err) console.log(files)
}) 

打印结果为:[ ‘fs.js’, ‘test’, ‘test.txt’ ]。 如果想获取更详细的信息,可以传入配置对象,将 withFileTypes 设置为 true

fs.readdir('../node', { withFileTypes: true }, (err, files) => {if (!err) console.log(files)
}) 

那么打印的结果就会通过 Symbol(type) 的值来表明是文件(值为 1),还是文件夹(值为 2),关于 Symbol 的介绍可参看 Symbol 详解

现在还有个问题,就是 test 文件夹里的 index.txt 文件没有被读取到,如果想读取 node 目录下的所有文件,可以通过递归读取:

function readDirRecursive(path) {fs.readdir(path, { withFileTypes: true }, (err, files) => {if (err) returnfiles.forEach(item => {if (item.isDirectory()) {readDirRecursive(`${path}/${item.name}`)} else {console.log(item.name)}})})
}
readDirRecursive('../node') 

我们也需要将 withFileTypes 设置为 true,这样返回的 files 数组中的元素才是一个个对象,并且有 name 属性和 isDirectory() 方法可以判断是否为文件夹。

重命名文件夹/文件

fs.rename() 可以重命名文件夹或文件,第 1 个参数为旧名称,第 2 个参数传新名称即可:

fs.rename('./test', './juejin', err => console.log(err)) 

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

猜你喜欢

转载自blog.csdn.net/Android_boom/article/details/129878924