Node.js 内置模块fs文件系统操作示例详解
亦黑迷失 人气:0File system 文件系统
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 个参数为用于指定配置的可选对象,其中有之前介绍过的两个属性 encoding
和 flag
,只不过这里 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) return fs.fstat(fd, (err, stats) => { if (err) return console.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) return files.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))
加载全部内容