🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
>[success] # 内置模块fs 1. fs是`File System`的缩写,表示文件系统,文件读取、写入、复制、重命名、删除、文件权限的设置等一系列的操作方法 2. 这些API大多数都提供三种操作方式 * **同步操作文件**:代码会被阻塞,不会继续执行; * **异步回调函数操作文件**:代码不会被阻塞,需要传入回调函数,当获取到结果时,回调函数被执行 * **异步Promise操作文件**:代码不会被阻塞,通过 fs.promises 调用方法操作,会返回一个Promise,可以通过then、catch进行处理 3. 关于`fs.promises` 风格,在 Node.js 14.x 版本中,许多 `fs` 模块的 API 都添加了 `fs.promises` 的支持,这意味着您可以使用类似 `fs.promises.readFile` 这样的 Promise 风格的异步 API 来代替传统的回调函数式异步 API。使用 `fs.promises` 可以方便地使用 async/await 语法糖来处理异步操作,从而使异步编程更加简单易懂。 但是,您需要注意的是,并非所有的 `fs` 模块的 API 都支持 `fs.promises`。例如,`fs.read` 和 `fs.write` 这两个 API 就不支持 `fs.promises`,因此只能使用传统的回调函数式异步 API 或者同步 API 来进行操作。 如果您需要使用同步 API,可以使用同名同参数的 API 来代替异步 API。例如,使用 `fs.readFileSync` 来代替 `fs.promises.readFile`,使用 `fs.mkdirSync` 来代替 `fs.promises.mkdir`。需要注意的是,同步 API 会阻塞程序的执行,因此需要谨慎使用,特别是在 IO 密集型的场景下。 4. 正常的同步api 和 通过async await 实现的同步`fs.promises` 有什么区别?举个例子 ~~~ const fs = require('fs') async function readA() { const a = await fs.promises.readFile('a.txt') console.log(a) } const b = fs.readFileSync('a.txt') readA() console.log(b) ~~~ 变量 b 是通过 `fs.readFileSync` 同步地读取文件内容得到的,因此当执行到 `console.log(b)` 时,**文件内容已经被完全读取并存储在变量 b 中,可以直接输出**。 相比之下,变量 a 是通过 `fs.promises.readFile` 异步地读取文件内容得到的。在执行到 `readA()` 时,**虽然异步读取文件的操作已经开始,但此时文件内容还没有被完全读取并存储在变量 a 中**,使用了 `await `的异步操作本质上仍然是异步的,只不过是使用了 `Promise `和 `async/await` 语法糖来简化异步编程的复杂度。因此,虽然使用了 `await`,变量 a 仍然是异步读取文件得到的结果,和 b 的同步读取结果有本质的区别。 具体来说,使用 `await `的异步操作会立即返回一个 `Promise `对象,在异步操作完成后,`Promise `对象的状态会从 `pending `变为 `fulfilled `或 `rejected`,并将异步操作的结果传递给 `Promise `的 then 方法或者 `catch `方法。在 `await `后的表达式中,如果 `Promise `的状态为 `fulfilled`,则会返回 `Promise `绑定的值;如果 `Promise `的状态为 `rejected`,则会抛出 `Promise `绑定的错误。 因此,在这段代码中,当执行到 `await fs.promises.readFile('a.txt')` 时,异步读取文件操作已经开始,但由于此时文件内容还未被完全读取,因此 `await `表达式会暂停代码的执行,直到异步操作完成并返回 `Promise `对象后,再继续执行下面的代码因此**变量 a 是异步读取文件的结果,和变量 b 的同步读取结果有本质的区别。** | API | 描述 | 同步/异步 | fs.promises 调用方法 | | --- | --- | --- | --- | | fs.readFile(path \[, options\], callback) | 异步读取文件内容 | 异步 | fs.promises.readFile(path \[, options\]) | | fs.readFileSync(path \[, options\]) | 同步读取文件内容 | 同步 | fs.promises.readFile(path \[, options\]) | | fs.writeFile(file, data \[, options\], callback) | 异步写入文件内容 | 异步 | fs.promises.writeFile(file, data \[, options\]) | | fs.writeFileSync(file, data \[, options\]) | 同步写入文件内容 | 同步 | fs.promises.writeFile(file, data \[, options\]) | | fs.mkdir(path \[, options\], callback) | 异步创建目录 | 异步 | fs.promises.mkdir(path \[, options\]) | | fs.mkdirSync(path \[, options\]) | 同步创建目录 | 同步 | fs.promises.mkdir(path \[, options\]) | | fs.readdir(path \[, options\], callback) | 异步读取目录下的所有文件和子目录 | 异步 | fs.promises.readdir(path \[, options\]) | | fs.readdirSync(path \[, options\]) | 同步读取目录下的所有文件和子目录 | 同步 | fs.promises.readdir(path \[, options\]) | | fs.stat(path, callback) | 异步获取文件或目录的状态信息 | 异步 | fs.promises.stat(path) | | fs.statSync(path) | 同步获取文件或目录的状态信息 | 同步 | fs.promises.stat(path) | | fs.rename(oldPath, newPath, callback) | 异步重命名文件 | 异步 | fs.promises.rename(oldPath, newPath) | | fs.renameSync(oldPath, newPath) | 同步重命名文件 | 同步 | fs.promises.rename(oldPath, newPath) | | fs.unlink(path, callback) | 异步删除文件 | 异步 | fs.promises.unlink(path) | | fs.unlinkSync(path) | 同步删除文件 | 同步 | fs.promises.unlink(path) | | fs.rmdir(path, callback) | 异步删除目录 | 异步 | fs.promises.rmdir(path) | | fs.rmdirSync(path) | 同步删除目录 | 同步 | fs.promises.rmdir(path) | | fs.createReadStream(path \[, options\]) | 创建可读流 | 异步 | fs.promises.createReadStream(path \[, options\]) | | fs.createWriteStream(path \[, options\]) | 创建可写流 | 异步 | fs.promises.createWriteStream(path \[, options\]) | | fs.watchFile(filename \[, options\], listener) | 监听文件变化 | 异步 | \- | | fs.unwatchFile(filename \[, listener\]) | 取消监听文件变化 | 异步 | \- | | fs.watch(filename \[, options\], listener) | 监听文件系统事件 | 异步 | \- | | fs.access(path \[, mode\], callback) | 测试用户对 path 指定的文件或目录的权限 | 异步 | fs.promises.access(path \[, mode\]) | | fs.accessSync(path \[, mode\]) | 测试用户对 path 指定的文件或目录的权限 | 同步 | fs.promises.access(path \[, mode\]) | >[danger] ##### 知识补充 `fs.read` 和 `fs.write` 这两个 API 并不支持 `fs.promises` 的主要原因是它们是基于底层操作系统提供的文件读写接口的,而这些接口通常是异步的,因此无法直接使用 Promise 风格的 API。 这两个 API 都是使用回调函数的方式实现异步操作的。例如,`fs.read` 方法的语法如下: ~~~javascript fs.read(fd, buffer, offset, length, position, callback) ~~~ 其中,`callback` 参数是一个回调函数,它在文件读取完成后被调用,通常会将读取到的数据和读取字节数作为参数传递给回调函数。类似地,`fs.write` 方法也是使用回调函数的方式实现异步操作的,它的语法如下: ~~~javascript fs.write(fd, buffer, offset, length, position, callback) ~~~ 由于这两个 API 是基于底层操作系统提供的接口实现的,因此无法直接使用 Promise 风格的 API。如果您希望使用 Promise 风格的 API,可以考虑使用 `fs.promises.readFile` 和 `fs.promises.writeFile` 这两个方法来实现文件读写操作,它们都是支持 Promise 风格的异步 API。 ***** `fs.write` 和 `fs.writeFile` 两者的主要区别在于: 1. 使用方式不同 `fs.write` 方法需要打开文件描述符,然后通过该文件描述符进行写入操作。它的语法如下: ~~~javascript fs.write(fd, buffer[, offset[, length[, position]]], callback) ~~~ 其中,`fd` 是文件描述符,`buffer` 是一个 `Buffer` 或 `Uint8Array` 对象,`offset` 和 `length` 分别表示要写入的数据在 `buffer` 中的偏移量和长度,`position` 表示写入的起始位置。最后一个参数是回调函数,它在写入完成后被调用。 相比之下,`fs.writeFile` 方法则更加简单,只需要指定文件路径和要写入的数据即可。它的语法如下: ~~~javascript fs.writeFile(file, data[, options], callback) ~~~ 其中,`file` 是文件路径,`data` 是要写入的数据,`options` 是一个可选的配置对象,`callback` 是写入完成后的回调函数。 2. 写入方式不同 `fs.write` 方法是基于文件描述符进行写入操作的,它可以使用多种写入方式,例如: * `fs.write(fd, buffer, offset, length, position, callback)`:向指定位置写入数据。 * `fs.write(fd, data, callback)`:向当前文件指针位置写入数据。 * `fs.write(fd, buffer[, offset[, length[, position]]])`:向当前文件指针位置写入数据,并将文件指针移动到写入数据的末尾。 相比之下,`fs.writeFile` 方法只支持一种写入方式,即向文件中写入指定的数据,如果文件已经存在,则会覆盖原有的数据。 因此,如果您需要对一个已经打开的文件进行写入操作,或者需要对一个文件进行复杂的写入操作(例如随机访问或追加写入),则应该使用 `fs.write` 方法。如果您只需要向文件中写入指定的数据,并且不需要进行复杂的写入操作,则可以使用 `fs.writeFile` 方法。