文件系统#

稳定性: 2 - 稳定

源码: lib/fs.js

node:fs 模块可以让你以一种模仿标准 POSIX 函数的方式与文件系统进行交互。

使用基于 Promise 的 API

import * as fs from 'node:fs/promises';const fs = require('node:fs/promises');

使用回调函数和同步 API

import * as fs from 'node:fs';const fs = require('node:fs');

所有文件系统操作都有同步、回调函数和基于 Promise 的形式,并且可以使用 CommonJS 语法和 ES6 模块(ESM)访问。

Promise 示例#

基于 Promise 的操作会返回一个 Promise,该 Promise 在异步操作完成时被兑现。

import { unlink } from 'node:fs/promises';

try {
  await unlink('/tmp/hello');
  console.log('successfully deleted /tmp/hello');
} catch (error) {
  console.error('there was an error:', error.message);
}const { unlink } = require('node:fs/promises');

(async function(path) {
  try {
    await unlink(path);
    console.log(`successfully deleted ${path}`);
  } catch (error) {
    console.error('there was an error:', error.message);
  }
})('/tmp/hello');

回调函数示例#

回调函数形式将完成回调函数作为其最后一个参数,并异步调用该操作。 传递给完成回调的参数取决于方法,但第一个参数始终保留用于异常。 如果操作成功完成,则第一个参数为 nullundefined

import { unlink } from 'node:fs';

unlink('/tmp/hello', (err) => {
  if (err) throw err;
  console.log('successfully deleted /tmp/hello');
});const { unlink } = require('node:fs');

unlink('/tmp/hello', (err) => {
  if (err) throw err;
  console.log('successfully deleted /tmp/hello');
});

当需要最大性能(无论是在执行时间还是在内存分配方面)时,node:fs 模块 API 的基于回调函数的版本优于 Promise API 的使用。

同步示例#

同步 API 会阻塞 Node.js 事件循环,并阻止进一步的 JavaScript 执行,直到操作完成。 异常会立即抛出,并且可以使用 try…catch 处理,也可以允许其冒泡。

import { unlinkSync } from 'node:fs';

try {
  unlinkSync('/tmp/hello');
  console.log('successfully deleted /tmp/hello');
} catch (err) {
  // handle the error
}const { unlinkSync } = require('node:fs');

try {
  unlinkSync('/tmp/hello');
  console.log('successfully deleted /tmp/hello');
} catch (err) {
  // handle the error
}

Promise API#

fs/promises API 提供了返回 Promise 的异步文件系统方法。

Promise API 使用底层 Node.js 线程池在事件循环线程之外执行文件系统操作。 这些操作不是同步的,也不是线程安全的。 在对同一文件执行多个并发修改时,必须小心,否则可能会发生数据损坏。

类: FileHandle#

<FileHandle> 对象是数值文件描述符的对象包装器。

<FileHandle> 对象的实例由 fsPromises.open() 方法创建。

所有 <FileHandle> 对象都是 <EventEmitter>

如果未使用 filehandle.close() 方法关闭 <FileHandle>,它将尝试自动关闭文件描述符并发出进程警告,以帮助防止内存泄漏。 请不要依赖此行为,因为它可能不可靠,并且文件可能未关闭。 相反,始终显式关闭 <FileHandle>。 Node.js 可能会在将来更改此行为。

事件: 'close'#

<FileHandle> 已关闭且无法再使用时,会发出 'close' 事件。

filehandle.appendFile(data[, options])#

filehandle.writeFile() 的别名。

在文件句柄上操作时,模式无法从使用 fsPromises.open() 设置的值更改。 因此,这等效于 filehandle.writeFile()

filehandle.chmod(mode)#
  • mode <integer> 文件模式位掩码。
  • 返回: <Promise> 成功时兑现为 undefined

修改文件上的权限。 参见 chmod(2)

filehandle.chown(uid, gid)#
  • uid <integer> 文件新所有者的用户 ID。
  • gid <integer> 文件新组的组 ID。
  • 返回: <Promise> 成功时兑现为 undefined

更改文件的所有权。 chown(2) 的包装器。

filehandle.close()#
  • 返回: <Promise> 成功时兑现为 undefined

关闭文件句柄,等待句柄上的任何挂起操作完成。

import { open } from 'node:fs/promises';

let filehandle;
try {
  filehandle = await open('thefile.txt', 'r');
} finally {
  await filehandle?.close();
} 
filehandle.createReadStream([options])#

options 可以包含 startend 值,以从文件中读取一系列字节,而不是整个文件。 startend 都是包含性的,并且从 0 开始计数,允许的值在 [0, Number.MAX_SAFE_INTEGER] 范围内。 如果省略或 undefined start,则 filehandle.createReadStream() 从当前文件位置按顺序读取。 encoding 可以是 <Buffer> 接受的任何一种。

如果 FileHandle 指向仅支持阻塞读取的字符设备(例如键盘或声卡),则读取操作在数据可用之前不会完成。 这可以防止进程退出和流自然关闭。

默认情况下,流在销毁后会发出 'close' 事件。 将 emitClose 选项设置为 false 以更改此行为。

import { open } from 'node:fs/promises';

const fd = await open('/dev/input/event0');
// Create a stream from some character device.
const stream = fd.createReadStream();
setTimeout(() => {
  stream.close(); // This may not close the stream.
  // Artificially marking end-of-stream, as if the underlying resource had
  // indicated end-of-file by itself, allows the stream to close.
  // This does not cancel pending read operations, and if there is such an
  // operation, the process may still not be able to exit successfully
  // until it finishes.
  stream.push(null);
  stream.read(0);
}, 100); 

如果 autoClose 为 false,则即使出现错误,文件描述符也不会关闭。 应用程序有责任关闭它并确保没有文件描述符泄漏。 如果 autoClose 设置为 true(默认行为),则在 'error''end' 上,文件描述符将自动关闭。

一个读取 100 字节长文件的最后 10 个字节的示例

import { open } from 'node:fs/promises';

const fd = await open('sample.txt');
fd.createReadStream({ start: 90, end: 99 }); 
filehandle.createWriteStream([options])#

options 还可以包含一个 start 选项,以允许在文件开头之后的某个位置写入数据,允许的值在 [0, Number.MAX_SAFE_INTEGER] 范围内。 修改文件而不是替换文件可能需要将 flags open 选项设置为 r+ 而不是默认的 rencoding 可以是 <Buffer> 接受的任何一种。

如果 autoClose'error''finish' 上设置为 true(默认行为),则文件描述符将自动关闭。 如果 autoClose 为 false,则即使出现错误,文件描述符也不会关闭。 应用程序有责任关闭它并确保没有文件描述符泄漏。

默认情况下,流在销毁后会发出 'close' 事件。 将 emitClose 选项设置为 false 以更改此行为。

filehandle.datasync()#
  • 返回: <Promise> 成功时兑现为 undefined

强制与该文件关联的所有当前排队的 I/O 操作到操作系统的同步 I/O 完成状态。 有关详细信息,请参阅 POSIX fdatasync(2) 文档。

filehandle.sync 不同,此方法不刷新修改后的元数据。

filehandle.fd#
filehandle.read(buffer, offset, length, position)#
  • buffer <Buffer> | <TypedArray> | <DataView> 将要被读取的文件数据填充的缓冲区。
  • offset <integer> 在缓冲区中开始填充的位置。默认值: 0
  • length <integer> 要读取的字节数。默认值: buffer.byteLength - offset
  • position <integer> | <bigint> | <null> 开始从文件中读取数据的位置。如果为 null-1,则将从当前文件位置读取数据,并且该位置将被更新。 如果 position 是一个非负整数,则当前文件位置将保持不变。默认值:: null
  • 返回: <Promise> 成功时会解析为一个包含两个属性的对象

从文件中读取数据并将其存储在给定的缓冲区中。

如果文件没有被并发修改,当读取的字节数为零时,达到文件末尾。

filehandle.read([options])#
  • options <Object>
    • buffer <Buffer> | <TypedArray> | <DataView> 将要被读取的文件数据填充的缓冲区。默认值: Buffer.alloc(16384)
    • offset <integer> 在缓冲区中开始填充的位置。默认值: 0
    • length <integer> 要读取的字节数。默认值: buffer.byteLength - offset
    • position <integer> | <bigint> | <null> 开始从文件中读取数据的位置。如果为 null-1,则将从当前文件位置读取数据,并且该位置将被更新。 如果 position 是一个非负整数,则当前文件位置将保持不变。默认值:: null
  • 返回: <Promise> 成功时会解析为一个包含两个属性的对象

从文件中读取数据并将其存储在给定的缓冲区中。

如果文件没有被并发修改,当读取的字节数为零时,达到文件末尾。

filehandle.read(buffer[, options])#
  • buffer <Buffer> | <TypedArray> | <DataView> 将要被读取的文件数据填充的缓冲区。
  • options <Object>
    • offset <integer> 在缓冲区中开始填充的位置。默认值: 0
    • length <integer> 要读取的字节数。默认值: buffer.byteLength - offset
    • position <integer> | <bigint> | <null> 开始从文件中读取数据的位置。如果为 null-1,则将从当前文件位置读取数据,并且该位置将被更新。 如果 position 是一个非负整数,则当前文件位置将保持不变。默认值:: null
  • 返回: <Promise> 成功时会解析为一个包含两个属性的对象

从文件中读取数据并将其存储在给定的缓冲区中。

如果文件没有被并发修改,当读取的字节数为零时,达到文件末尾。

filehandle.readableWebStream()#

返回一个面向字节的 ReadableStream,可用于读取文件内容。

如果此方法被多次调用,或者在 FileHandle 关闭或正在关闭之后调用,将会抛出一个错误。

import {
  open,
} from 'node:fs/promises';

const file = await open('./some/file/to/read');

for await (const chunk of file.readableWebStream())
  console.log(chunk);

await file.close();const {
  open,
} = require('node:fs/promises');

(async () => {
  const file = await open('./some/file/to/read');

  for await (const chunk of file.readableWebStream())
    console.log(chunk);

  await file.close();
})();

虽然 ReadableStream 将读取文件到完成,但它不会自动关闭 FileHandle。 用户代码仍然必须调用 fileHandle.close() 方法。

filehandle.readFile(options)#
  • options <Object> | <string>
  • 返回: <Promise> 成功读取文件内容时解析。 如果未指定编码(使用 options.encoding),则数据作为 <Buffer> 对象返回。 否则,数据将是一个字符串。

异步读取文件的全部内容。

如果 options 是一个字符串,那么它指定了 encoding

<FileHandle> 必须支持读取。

如果对文件句柄进行了一个或多个 filehandle.read() 调用,然后进行了 filehandle.readFile() 调用,则数据将从当前位置读取到文件末尾。 它并不总是从文件的开头读取。

filehandle.readLines([options])#

用于创建 readline 接口和流式传输文件内容的简便方法。 有关选项,请参见 filehandle.createReadStream()

import { open } from 'node:fs/promises';

const file = await open('./some/file/to/read');

for await (const line of file.readLines()) {
  console.log(line);
}const { open } = require('node:fs/promises');

(async () => {
  const file = await open('./some/file/to/read');

  for await (const line of file.readLines()) {
    console.log(line);
  }
})();
filehandle.readv(buffers[, position])#

从文件读取并写入到 <ArrayBufferView> 的数组中

filehandle.stat([options])#
filehandle.sync()#
  • 返回: <Promise> 成功时兑现为 undefined

请求将打开文件描述符的所有数据刷新到存储设备。 具体实现取决于操作系统和设备。 有关更多详细信息,请参阅 POSIX fsync(2) 文档。

filehandle.truncate(len)#

截断文件。

如果文件大于 len 字节,则该文件中仅保留前 len 个字节。

以下示例仅保留文件的前四个字节

import { open } from 'node:fs/promises';

let filehandle = null;
try {
  filehandle = await open('temp.txt', 'r+');
  await filehandle.truncate(4);
} finally {
  await filehandle?.close();
} 

如果文件之前短于 len 字节,则将其扩展,并且扩展的部分用空字节 ('\0') 填充

如果 len 为负数,则将使用 0

filehandle.utimes(atime, mtime)#

更改由 <FileHandle> 引用的对象的文件系统时间戳,然后在成功时解析 promise,不带任何参数。

filehandle.write(buffer, offset[, length[, position]])#
  • buffer <Buffer> | <TypedArray> | <DataView>
  • offset <integer>buffer 内部开始写入数据的起始位置。
  • length <integer>buffer 中要写入的字节数。默认值: buffer.byteLength - offset
  • position <integer> | <null> 从文件开头到 buffer 中的数据应该写入的偏移量。 如果 position 不是 number,数据将写入到当前位置。 有关更多详细信息,请参阅 POSIX pwrite(2) 文档。 默认值: null
  • 返回: <Promise>

buffer 写入文件。

该 promise 完成时会带有一个包含两个属性的对象

在没有等待 promise 完成(或被拒绝)的情况下,多次在同一文件上使用 filehandle.write() 是不安全的。 对于这种情况,请使用 filehandle.createWriteStream()

在 Linux 上,当文件以追加模式打开时,位置写入不起作用。 内核忽略位置参数,总是将数据追加到文件末尾。

filehandle.write(buffer[, options])#

buffer 写入文件。

与上面的 filehandle.write 函数类似,此版本采用一个可选的 options 对象。 如果未指定 options 对象,它将默认使用上述值。

filehandle.write(string[, position[, encoding]])#
  • string <string>
  • position <integer> | <null> 从文件开头到 string 中的数据应该写入的偏移量。 如果 position 不是 number,数据将写入到当前位置。 有关更多详细信息,请参阅 POSIX pwrite(2) 文档。 默认值: null
  • encoding <string> 期望的字符串编码。 默认值: 'utf8'
  • 返回: <Promise>

string 写入文件。 如果 string 不是字符串,则 promise 会因错误而被拒绝。

该 promise 完成时会带有一个包含两个属性的对象

  • bytesWritten <integer> 写入的字节数
  • buffer <string> 对已写入的 string 的引用。

在没有等待 promise 完成(或被拒绝)的情况下,多次在同一文件上使用 filehandle.write() 是不安全的。 对于这种情况,请使用 filehandle.createWriteStream()

在 Linux 上,当文件以追加模式打开时,位置写入不起作用。 内核忽略位置参数,总是将数据追加到文件末尾。

filehandle.writeFile(data, options)#

异步地将数据写入文件,如果文件已存在,则替换该文件。 data 可以是字符串、缓冲区、<AsyncIterable><Iterable> 对象。 promise 完成时没有参数。

如果 options 是一个字符串,那么它指定了 encoding

<FileHandle> 必须支持写入。

在没有等待 promise 完成(或被拒绝)的情况下,多次在同一文件上使用 filehandle.writeFile() 是不安全的。

如果对文件句柄进行一次或多次 filehandle.write() 调用,然后再进行 filehandle.writeFile() 调用,则数据将从当前位置写入到文件末尾。 它并不总是从文件开头写入。

filehandle.writev(buffers[, position])#

<ArrayBufferView> 的数组写入文件。

该 promise 完成时会带有一个包含两个属性的对象

在没有等待 promise 完成(或被拒绝)的情况下,多次在同一文件上调用 writev() 是不安全的。

在 Linux 上,当文件以追加模式打开时,位置写入不起作用。 内核忽略位置参数,总是将数据追加到文件末尾。

filehandle[Symbol.asyncDispose]()#

稳定性: 1 - 实验性

filehandle.close() 的别名。

fsPromises.access(path[, mode])#

测试用户对 path 指定的文件或目录的权限。 mode 参数是一个可选的整数,指定要执行的可访问性检查。 mode 应该是值 fs.constants.F_OK 或由 fs.constants.R_OKfs.constants.W_OKfs.constants.X_OK 中的任何一个的按位 OR 组成的掩码(例如 fs.constants.W_OK | fs.constants.R_OK)。 检查文件访问常量,了解 mode 的可能值。

如果可访问性检查成功,则 promise 将完成,不带任何值。 如果任何可访问性检查失败,则 promise 将因 <Error> 对象而被拒绝。 以下示例检查当前进程是否可以读取和写入文件 /etc/passwd

import { access, constants } from 'node:fs/promises';

try {
  await access('/etc/passwd', constants.R_OK | constants.W_OK);
  console.log('can access');
} catch {
  console.error('cannot access');
} 

不建议使用 fsPromises.access() 在调用 fsPromises.open() 之前检查文件的可访问性。 这样做会引入竞争条件,因为其他进程可能会在两次调用之间更改文件的状态。 相反,用户代码应该直接打开/读取/写入文件,并处理在文件不可访问时引发的错误。

fsPromises.appendFile(path, data[, options])#

异步地将数据追加到文件,如果文件尚不存在,则创建该文件。 data 可以是字符串或 <Buffer>

如果 options 是一个字符串,那么它指定了 encoding

mode 选项仅影响新创建的文件。 有关更多详细信息,请参见 fs.open()

path 可以指定为已打开以进行追加的 <FileHandle>(使用 fsPromises.open())。

fsPromises.chmod(path, mode)#

更改文件的权限。

fsPromises.chown(path, uid, gid)#

更改文件的所有权。

fsPromises.copyFile(src, dest[, mode])#

  • src <string> | <Buffer> | <URL> 要复制的源文件名
  • dest <string> | <Buffer> | <URL> 复制操作的目标文件名
  • mode <integer> 可选的修饰符,用于指定复制操作的行为。可以创建一个由两个或多个值按位或运算组成的掩码(例如 fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE)。默认值: 0
    • fs.constants.COPYFILE_EXCL:如果 dest 已经存在,复制操作将失败。
    • fs.constants.COPYFILE_FICLONE:复制操作将尝试创建一个写时复制的 reflink。如果平台不支持写时复制,则使用备用复制机制。
    • fs.constants.COPYFILE_FICLONE_FORCE:复制操作将尝试创建一个写时复制的 reflink。如果平台不支持写时复制,则操作将失败。
  • 返回: <Promise> 成功时兑现为 undefined

异步地将 src 复制到 dest。 默认情况下,如果 dest 已经存在,则会被覆盖。

不保证复制操作的原子性。如果在打开目标文件以进行写入后发生错误,将尝试删除目标文件。

import { copyFile, constants } from 'node:fs/promises';

try {
  await copyFile('source.txt', 'destination.txt');
  console.log('source.txt was copied to destination.txt');
} catch {
  console.error('The file could not be copied');
}

// By using COPYFILE_EXCL, the operation will fail if destination.txt exists.
try {
  await copyFile('source.txt', 'destination.txt', constants.COPYFILE_EXCL);
  console.log('source.txt was copied to destination.txt');
} catch {
  console.error('The file could not be copied');
} 

fsPromises.cp(src, dest[, options])#

  • src <string> | <URL> 要复制的源路径。
  • dest <string> | <URL> 要复制到的目标路径。
  • options <Object>
    • dereference <boolean> 解引用符号链接。 默认值: false
    • errorOnExist <boolean>forcefalse 并且目标存在时,抛出一个错误。 默认值: false
    • filter <Function> 用于过滤复制的文件/目录的函数。 返回 true 以复制该项,返回 false 以忽略它。 忽略目录时,其所有内容也将被跳过。 也可以返回一个解析为 truefalsePromise默认值: undefined
      • src <string> 要复制的源路径。
      • dest <string> 要复制到的目标路径。
      • 返回: <boolean> | <Promise> 一个可以强制转换为 boolean 的值,或者一个解析为此值的 Promise
    • force <boolean> 覆盖现有文件或目录。如果将此选项设置为 false 且目标存在,则复制操作将忽略错误。使用 errorOnExist 选项来更改此行为。默认值: true
    • mode <integer> 复制操作的修饰符。默认值: 0。 请参阅 fsPromises.copyFile()mode 标志。
    • preserveTimestamps <boolean>true 时,将保留来自 src 的时间戳。默认值: false
    • recursive <boolean> 递归复制目录。 默认值: false
    • verbatimSymlinks <boolean>true 时,将跳过符号链接的路径解析。默认值: false
  • 返回: <Promise> 成功时兑现为 undefined

异步地将整个目录结构从 src 复制到 dest,包括子目录和文件。

当将一个目录复制到另一个目录时,不支持 glob,并且行为类似于 cp dir1/ dir2/

fsPromises.glob(pattern[, options])#

  • pattern <string> | <string[]>
  • options <Object>
    • cwd <string> 当前工作目录。 默认值: process.cwd()
    • exclude <Function> | <string[]> 用于过滤掉文件/目录的函数或要排除的 glob 模式列表。 如果提供一个函数,返回 true 以排除该项,返回 false 以包含该项。 默认值: undefined
    • withFileTypes <boolean> 如果 glob 应该将路径作为 Dirent 返回,则为 true,否则为 false默认值: false
  • 返回: <AsyncIterator> 一个 AsyncIterator,它产生与模式匹配的文件的路径。
import { glob } from 'node:fs/promises';

for await (const entry of glob('**/*.js'))
  console.log(entry);const { glob } = require('node:fs/promises');

(async () => {
  for await (const entry of glob('**/*.js'))
    console.log(entry);
})();

fsPromises.lchmod(path, mode)#

稳定性: 0 - 已弃用

更改符号链接的权限。

此方法仅在 macOS 上实现。

fsPromises.lchown(path, uid, gid)#

更改符号链接的所有权。

fsPromises.lutimes(path, atime, mtime)#

以与 fsPromises.utimes() 相同的方式更改文件的访问和修改时间,不同之处在于,如果路径引用符号链接,则不会解引用该链接:而是更改符号链接本身的时间戳。

fsPromises.link(existingPath, newPath)#

创建一个从 existingPathnewPath 的新链接。 有关更多详细信息,请参阅 POSIX link(2) 文档。

fsPromises.lstat(path[, options])#

等同于 fsPromises.stat(),除非 path 引用符号链接,在这种情况下,将对链接本身进行 stat,而不是对其引用的文件进行 stat。 有关更多详细信息,请参阅 POSIX lstat(2) 文档。

fsPromises.mkdir(path[, options])#

异步地创建一个目录。

可选的 options 参数可以是一个指定 mode(权限和粘滞位)的整数,或者是一个具有 mode 属性和 recursive 属性的对象,指示是否应创建父目录。当 path 是已存在的目录时调用 fsPromises.mkdir() 仅当 recursive 为 false 时才会导致拒绝。

import { mkdir } from 'node:fs/promises';

try {
  const projectFolder = new URL('./test/project/', import.meta.url);
  const createDir = await mkdir(projectFolder, { recursive: true });

  console.log(`created ${createDir}`);
} catch (err) {
  console.error(err.message);
}const { mkdir } = require('node:fs/promises');
const { join } = require('node:path');

async function makeDirectory() {
  const projectFolder = join(__dirname, 'test', 'project');
  const dirCreation = await mkdir(projectFolder, { recursive: true });

  console.log(dirCreation);
  return dirCreation;
}

makeDirectory().catch(console.error);

fsPromises.mkdtemp(prefix[, options])#

创建一个唯一的临时目录。 通过将六个随机字符附加到提供的 prefix 的末尾来生成唯一的目录名称。 由于平台不一致,请避免在 prefix 中使用尾随的 X 字符。 某些平台(尤其是 BSD)可以返回超过六个随机字符,并将 prefix 中的尾随 X 字符替换为随机字符。

可选的 options 参数可以是一个指定编码的字符串,或者是一个具有指定要使用的字符编码的 encoding 属性的对象。

import { mkdtemp } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';

try {
  await mkdtemp(join(tmpdir(), 'foo-'));
} catch (err) {
  console.error(err);
} 

fsPromises.mkdtemp() 方法会将六个随机选择的字符直接附加到 prefix 字符串。 例如,给定目录 /tmp,如果目的是在 /tmp创建一个临时目录,则 prefix 必须以尾随的平台特定路径分隔符结尾 (require('node:path').sep)。

fsPromises.open(path, flags[, mode])#

打开一个 <FileHandle>

有关更多详细信息,请参阅 POSIX open(2) 文档。

某些字符 (< > : " / \ | ? *) 在 Windows 下保留,如命名文件、路径和命名空间所述。 在 NTFS 下,如果文件名包含冒号,则 Node.js 将打开文件系统流,如此 MSDN 页面所述。

fsPromises.opendir(path[, options])#

异步打开目录以进行迭代扫描。 有关更多详细信息,请参阅 POSIX opendir(3) 文档。

创建一个 <fs.Dir>,其中包含用于从目录读取和清理目录的所有其他函数。

encoding 选项设置打开目录和后续读取操作时 path 的编码。

使用异步迭代的示例

import { opendir } from 'node:fs/promises';

try {
  const dir = await opendir('./');
  for await (const dirent of dir)
    console.log(dirent.name);
} catch (err) {
  console.error(err);
} 

当使用异步迭代器时,在迭代器退出后,<fs.Dir> 对象将自动关闭。

fsPromises.readdir(path[, options])#

  • path <string> | <Buffer> | <URL>
  • options <string> | <Object>
    • encoding <string> 默认值: 'utf8'
    • withFileTypes <boolean> 默认值: false
    • recursive <boolean> 如果为 true,则递归读取目录的内容。 在递归模式下,它将列出所有文件、子文件和目录。 默认值: false
  • 返回: <Promise> 使用目录中文件名称的数组(不包括 '.''..')来实现。

读取目录的内容。

可选的 options 参数可以是一个指定编码的字符串,或者是一个具有 encoding 属性的对象,该属性指定用于文件名的字符编码。 如果 encoding 设置为 'buffer',则返回的文件名将作为 <Buffer> 对象传递。

如果 options.withFileTypes 设置为 true,则返回的数组将包含 <fs.Dirent> 对象。

import { readdir } from 'node:fs/promises';

try {
  const files = await readdir(path);
  for (const file of files)
    console.log(file);
} catch (err) {
  console.error(err);
} 

fsPromises.readFile(path[, options])#

异步读取文件的全部内容。

如果未指定编码(使用 options.encoding),则数据将作为 <Buffer> 对象返回。 否则,数据将是一个字符串。

如果 options 是一个字符串,那么它指定编码。

path 是目录时,fsPromises.readFile() 的行为是平台相关的。 在 macOS、Linux 和 Windows 上,Promise 将被拒绝并出现错误。 在 FreeBSD 上,将返回目录内容的表示形式。

读取位于运行代码的同一目录中的 package.json 文件的示例

import { readFile } from 'node:fs/promises';
try {
  const filePath = new URL('./package.json', import.meta.url);
  const contents = await readFile(filePath, { encoding: 'utf8' });
  console.log(contents);
} catch (err) {
  console.error(err.message);
}const { readFile } = require('node:fs/promises');
const { resolve } = require('node:path');
async function logFile() {
  try {
    const filePath = resolve('./package.json');
    const contents = await readFile(filePath, { encoding: 'utf8' });
    console.log(contents);
  } catch (err) {
    console.error(err.message);
  }
}
logFile();

可以使用 <AbortSignal> 中止正在进行的 readFile。 如果请求被中止,则返回的 Promise 将被拒绝并出现 AbortError

import { readFile } from 'node:fs/promises';

try {
  const controller = new AbortController();
  const { signal } = controller;
  const promise = readFile(fileName, { signal });

  // Abort the request before the promise settles.
  controller.abort();

  await promise;
} catch (err) {
  // When a request is aborted - err is an AbortError
  console.error(err);
} 

中止正在进行的请求不会中止单个操作系统请求,而是中止内部缓冲 fs.readFile 执行的。

任何指定的 <FileHandle> 都必须支持读取。

fsPromises.readlink(path[, options])#

读取 path 引用的符号链接的内容。 有关更多详细信息,请参阅 POSIX readlink(2) 文档。 成功后,Promise 使用 linkString 来实现。

可选的 options 参数可以是一个指定编码的字符串,或者是一个具有 encoding 属性的对象,该属性指定用于返回的链接路径的字符编码。 如果 encoding 设置为 'buffer',则返回的链接路径将作为 <Buffer> 对象传递。

fsPromises.realpath(path[, options])#

使用与 fs.realpath.native() 函数相同的语义确定 path 的实际位置。

仅支持可以转换为 UTF8 字符串的路径。

可选的 options 参数可以是一个指定编码的字符串,或者是一个具有 encoding 属性的对象,该属性指定用于路径的字符编码。 如果 encoding 设置为 'buffer',则返回的路径将作为 <Buffer> 对象传递。

在 Linux 上,当 Node.js 链接到 musl libc 时,必须将 procfs 文件系统挂载到 /proc 才能使此函数工作。 Glibc 没有此限制。

fsPromises.rename(oldPath, newPath)#

oldPath 重命名为 newPath

fsPromises.rmdir(path[, options])#

  • path <string> | <Buffer> | <URL>
  • options <Object>
    • maxRetries <integer> 如果遇到 EBUSYEMFILEENFILEENOTEMPTYEPERM 错误,Node.js 会重试该操作,并在每次尝试时线性退避等待 retryDelay 毫秒。 此选项表示重试次数。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 0
    • recursive <boolean> 如果为 true,则执行递归目录删除。 在递归模式下,操作会在失败时重试。 默认值: false已弃用。
    • retryDelay <integer> 重试之间等待的时间(以毫秒为单位)。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 100
  • 返回: <Promise> 成功时兑现为 undefined

删除由 path 标识的目录。

在文件(而不是目录)上使用 fsPromises.rmdir() 会导致 promise 被拒绝,Windows 上出现 ENOENT 错误,POSIX 上出现 ENOTDIR 错误。

要获得类似于 rm -rf Unix 命令的行为,请使用带有选项 { recursive: true, force: true }fsPromises.rm()

fsPromises.rm(path[, options])#

  • path <string> | <Buffer> | <URL>
  • options <Object>
    • force <boolean>true 时,如果 path 不存在,则忽略异常。 默认值: false
    • maxRetries <integer> 如果遇到 EBUSYEMFILEENFILEENOTEMPTYEPERM 错误,Node.js 会重试该操作,并在每次尝试时线性退避等待 retryDelay 毫秒。 此选项表示重试次数。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 0
    • recursive <boolean> 如果为 true,则执行递归目录删除。 在递归模式下,操作会在失败时重试。 默认值: false
    • retryDelay <integer> 重试之间等待的时间(以毫秒为单位)。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 100
  • 返回: <Promise> 成功时兑现为 undefined

删除文件和目录(以标准 POSIX rm 实用程序为模型)。

fsPromises.stat(path[, options])#

fsPromises.statfs(path[, options])#

fsPromises.symlink(target, path[, type])#

创建符号链接。

type 参数仅在 Windows 平台上使用,可以是 'dir''file''junction' 之一。 如果 type 参数为 null,Node.js 将自动检测 target 类型并使用 'file''dir'。 如果 target 不存在,将使用 'file'。 Windows 连接点要求目标路径是绝对路径。 使用 'junction' 时,target 参数将自动规范化为绝对路径。 NTFS 卷上的连接点只能指向目录。

fsPromises.truncate(path[, len])#

path 处的内容截断(缩短或延长长度)为 len 字节。

fsPromises.unlink(path)#

如果 path 指的是符号链接,则删除该链接,而不影响该链接指向的文件或目录。 如果 path 指的是不是符号链接的文件路径,则删除该文件。 有关更多详细信息,请参阅 POSIX unlink(2) 文档。

fsPromises.utimes(path, atime, mtime)#

更改 path 引用的对象的文件系统时间戳。

atimemtime 参数遵循以下规则

  • 值可以是表示 Unix 纪元时间的数字、Date 或类似 '123456789.0' 的数字字符串。
  • 如果该值无法转换为数字,或者为 NaNInfinity-Infinity,则会抛出 Error

fsPromises.watch(filename[, options])#

返回一个异步迭代器,该迭代器监视 filename 上的更改,其中 filename 是文件或目录。

const { watch } = require('node:fs/promises');

const ac = new AbortController();
const { signal } = ac;
setTimeout(() => ac.abort(), 10000);

(async () => {
  try {
    const watcher = watch(__filename, { signal });
    for await (const event of watcher)
      console.log(event);
  } catch (err) {
    if (err.name === 'AbortError')
      return;
    throw err;
  }
})(); 

在大多数平台上,每当文件名在目录中出现或消失时,都会发出 'rename'

fs.watch() 的所有注意事项也适用于 fsPromises.watch()

fsPromises.writeFile(file, data[, options])#

异步地将数据写入文件,如果文件已存在则替换该文件。 data 可以是字符串、buffer、<AsyncIterable><Iterable> 对象。

如果 data 是 buffer,则忽略 encoding 选项。

如果 options 是一个字符串,那么它指定编码。

mode 选项仅影响新创建的文件。 有关更多详细信息,请参见 fs.open()

任何指定的 <FileHandle> 都必须支持写入。

在没有等待 promise 被解决的情况下,多次在同一文件上使用 fsPromises.writeFile() 是不安全的。

fsPromises.readFile 类似 - fsPromises.writeFile 是一种便捷方法,它在内部执行多个 write 调用以写入传递给它的缓冲区。 对于性能敏感的代码,请考虑使用 fs.createWriteStream()filehandle.createWriteStream()

可以使用 <AbortSignal> 来取消 fsPromises.writeFile()。 取消是“尽力而为”,并且可能仍然会写入少量数据。

import { writeFile } from 'node:fs/promises';
import { Buffer } from 'node:buffer';

try {
  const controller = new AbortController();
  const { signal } = controller;
  const data = new Uint8Array(Buffer.from('Hello Node.js'));
  const promise = writeFile('message.txt', data, { signal });

  // Abort the request before the promise settles.
  controller.abort();

  await promise;
} catch (err) {
  // When a request is aborted - err is an AbortError
  console.error(err);
} 

中止正在进行的请求不会中止单个操作系统请求,而是中止 fs.writeFile 执行的内部缓冲。

fsPromises.constants#

返回一个对象,其中包含文件系统操作常用的常量。 该对象与 fs.constants 相同。 有关更多详细信息,请参阅FS 常量

回调 API#

回调 API 异步执行所有操作,不会阻塞事件循环,然后在完成或出错时调用回调函数。

回调 API 使用底层 Node.js 线程池在事件循环线程之外执行文件系统操作。 这些操作不是同步的或线程安全的。 在对同一文件执行多个并发修改时,必须小心,否则可能会发生数据损坏。

fs.access(path[, mode], callback)#

测试用户对 path 指定的文件或目录的权限。 mode 参数是一个可选的整数,指定要执行的可访问性检查。 mode 应该是值 fs.constants.F_OK 或由 fs.constants.R_OKfs.constants.W_OKfs.constants.X_OK 中的任何一个的按位 OR 组成的掩码(例如 fs.constants.W_OK | fs.constants.R_OK)。 检查文件访问常量,了解 mode 的可能值。

最后一个参数 callback 是一个回调函数,它在调用时会带有一个可能的错误参数。 如果任何可访问性检查失败,则错误参数将是一个 Error 对象。 以下示例检查 package.json 是否存在,以及它是否可读或可写。

import { access, constants } from 'node:fs';

const file = 'package.json';

// Check if the file exists in the current directory.
access(file, constants.F_OK, (err) => {
  console.log(`${file} ${err ? 'does not exist' : 'exists'}`);
});

// Check if the file is readable.
access(file, constants.R_OK, (err) => {
  console.log(`${file} ${err ? 'is not readable' : 'is readable'}`);
});

// Check if the file is writable.
access(file, constants.W_OK, (err) => {
  console.log(`${file} ${err ? 'is not writable' : 'is writable'}`);
});

// Check if the file is readable and writable.
access(file, constants.R_OK | constants.W_OK, (err) => {
  console.log(`${file} ${err ? 'is not' : 'is'} readable and writable`);
}); 

在调用 fs.open()fs.readFile()fs.writeFile() 之前,请勿使用 fs.access() 检查文件的可访问性。 这样做会引入竞争条件,因为其他进程可能会在两次调用之间更改文件的状态。 相反,用户代码应直接打开/读取/写入文件,并处理文件不可访问时引发的错误。

写入(不推荐)

import { access, open, close } from 'node:fs';

access('myfile', (err) => {
  if (!err) {
    console.error('myfile already exists');
    return;
  }

  open('myfile', 'wx', (err, fd) => {
    if (err) throw err;

    try {
      writeMyData(fd);
    } finally {
      close(fd, (err) => {
        if (err) throw err;
      });
    }
  });
}); 

写入(推荐)

import { open, close } from 'node:fs';

open('myfile', 'wx', (err, fd) => {
  if (err) {
    if (err.code === 'EEXIST') {
      console.error('myfile already exists');
      return;
    }

    throw err;
  }

  try {
    writeMyData(fd);
  } finally {
    close(fd, (err) => {
      if (err) throw err;
    });
  }
}); 

读取(不推荐)

import { access, open, close } from 'node:fs';
access('myfile', (err) => {
  if (err) {
    if (err.code === 'ENOENT') {
      console.error('myfile does not exist');
      return;
    }

    throw err;
  }

  open('myfile', 'r', (err, fd) => {
    if (err) throw err;

    try {
      readMyData(fd);
    } finally {
      close(fd, (err) => {
        if (err) throw err;
      });
    }
  });
}); 

读取(推荐)

import { open, close } from 'node:fs';

open('myfile', 'r', (err, fd) => {
  if (err) {
    if (err.code === 'ENOENT') {
      console.error('myfile does not exist');
      return;
    }

    throw err;
  }

  try {
    readMyData(fd);
  } finally {
    close(fd, (err) => {
      if (err) throw err;
    });
  }
}); 

上面的“不推荐”示例检查可访问性,然后使用该文件; “推荐”示例更好,因为它们直接使用该文件并处理错误(如果有)。

通常,只有在不直接使用文件的情况下,才检查文件的可访问性,例如,当其可访问性是来自另一个进程的信号时。

在 Windows 上,目录上的访问控制策略 (ACL) 可能会限制对文件或目录的访问。 但是,fs.access() 函数不检查 ACL,因此即使 ACL 限制用户读取或写入,也可能会报告路径可访问。

fs.appendFile(path, data[, options], callback)#

异步地将数据追加到文件,如果文件尚不存在,则创建该文件。 data 可以是字符串或 <Buffer>

mode 选项仅影响新创建的文件。 有关更多详细信息,请参见 fs.open()

import { appendFile } from 'node:fs';

appendFile('message.txt', 'data to append', (err) => {
  if (err) throw err;
  console.log('The "data to append" was appended to file!');
}); 

如果 options 是字符串,则它指定编码

import { appendFile } from 'node:fs';

appendFile('message.txt', 'data to append', 'utf8', callback); 

path 可以指定为已打开用于附加的数字文件描述符(使用 fs.open()fs.openSync())。 文件描述符不会自动关闭。

import { open, close, appendFile } from 'node:fs';

function closeFd(fd) {
  close(fd, (err) => {
    if (err) throw err;
  });
}

open('message.txt', 'a', (err, fd) => {
  if (err) throw err;

  try {
    appendFile(fd, 'data to append', 'utf8', (err) => {
      closeFd(fd);
      if (err) throw err;
    });
  } catch (err) {
    closeFd(fd);
    throw err;
  }
}); 

fs.chmod(path, mode, callback)#

异步更改文件的权限。 除了可能的异常之外,没有参数传递给完成回调。

有关更多详细信息,请参阅 POSIX chmod(2) 文档。

import { chmod } from 'node:fs';

chmod('my_file.txt', 0o775, (err) => {
  if (err) throw err;
  console.log('The permissions for file "my_file.txt" have been changed!');
}); 
文件模式#

fs.chmod()fs.chmodSync() 方法中使用的 mode 参数是一个数字位掩码,使用以下常量的逻辑 OR 创建

常量八进制描述
fs.constants.S_IRUSR0o400所有者可读
fs.constants.S_IWUSR0o200所有者可写
fs.constants.S_IXUSR0o100所有者可执行/搜索
fs.constants.S_IRGRP0o40组可读
fs.constants.S_IWGRP0o20组可写
fs.constants.S_IXGRP0o10组可执行/搜索
fs.constants.S_IROTH0o4其他人可读
fs.constants.S_IWOTH0o2其他人可写
fs.constants.S_IXOTH0o1其他人可执行/搜索

构造 mode 的一种更简单的方法是使用三个八进制数字的序列(例如 765)。 最左边的数字(示例中的 7)指定文件所有者的权限。 中间的数字(示例中的 6)指定组的权限。 最右边的数字(示例中的 5)指定其他人的权限。

数字描述
7读取、写入和执行
6读取和写入
5读取和执行
4只读
3写入和执行
2只写
1仅执行
0无权限

例如,八进制值 0o765 表示

  • 所有者可以读取、写入和执行该文件。
  • 该组可以读取和写入该文件。
  • 其他人可以读取和执行该文件。

当在使用文件模式的原始数字时,任何大于 0o777 的值都可能导致平台相关的行为,这些行为不支持一致地工作。 因此,S_ISVTXS_ISGIDS_ISUID 等常量不会在 fs.constants 中公开。

注意事项:在 Windows 上,只能更改写入权限,并且组、所有者或其他人的权限之间的区别未实现。

fs.chown(path, uid, gid, callback)#

异步更改文件的所有者和组。 除了可能的异常之外,没有参数传递给完成回调。

有关更多详细信息,请参阅 POSIX chown(2) 文档。

fs.close(fd[, callback])#

关闭文件描述符。 除了可能的异常之外,没有参数传递给完成回调。

在任何其他 fs 操作正在使用的任何文件描述符 (fd) 上调用 fs.close() 可能会导致未定义的行为。

有关更多详细信息,请参阅 POSIX close(2) 文档。

fs.copyFile(src, dest[, mode], callback)#

异步将 src 复制到 dest。 默认情况下,如果 dest 已经存在,则会被覆盖。 除了可能的异常之外,没有参数传递给回调函数。 Node.js 不保证复制操作的原子性。 如果在打开目标文件进行写入后发生错误,Node.js 将尝试删除目标文件。

mode 是一个可选的整数,用于指定复制操作的行为。 可以创建一个由两个或多个值的按位或组成的掩码(例如 fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE)。

  • fs.constants.COPYFILE_EXCL:如果 dest 已经存在,复制操作将失败。
  • fs.constants.COPYFILE_FICLONE:复制操作将尝试创建一个写时复制的 reflink。如果平台不支持写时复制,则使用备用复制机制。
  • fs.constants.COPYFILE_FICLONE_FORCE:复制操作将尝试创建一个写时复制的 reflink。如果平台不支持写时复制,则操作将失败。
import { copyFile, constants } from 'node:fs';

function callback(err) {
  if (err) throw err;
  console.log('source.txt was copied to destination.txt');
}

// destination.txt will be created or overwritten by default.
copyFile('source.txt', 'destination.txt', callback);

// By using COPYFILE_EXCL, the operation will fail if destination.txt exists.
copyFile('source.txt', 'destination.txt', constants.COPYFILE_EXCL, callback); 

fs.cp(src, dest[, options], callback)#

  • src <string> | <URL> 要复制的源路径。
  • dest <string> | <URL> 要复制到的目标路径。
  • options <Object>
    • dereference <boolean> 解引用符号链接。 默认值: false
    • errorOnExist <boolean>forcefalse 并且目标存在时,抛出一个错误。 默认值: false
    • filter <Function> 用于过滤复制的文件/目录的函数。 返回 true 以复制该项,返回 false 以忽略它。 忽略目录时,其所有内容也将被跳过。 也可以返回一个解析为 truefalsePromise默认值: undefined
      • src <string> 要复制的源路径。
      • dest <string> 要复制到的目标路径。
      • 返回: <boolean> | <Promise> 一个可以强制转换为 boolean 的值,或者一个解析为此值的 Promise
    • force <boolean> 覆盖现有文件或目录。如果将此选项设置为 false 且目标存在,则复制操作将忽略错误。使用 errorOnExist 选项来更改此行为。默认值: true
    • mode <integer> 复制操作的修饰符。 默认值: 0。 参见 fs.copyFile()mode 标志。
    • preserveTimestamps <boolean>true 时,将保留来自 src 的时间戳。默认值: false
    • recursive <boolean> 递归复制目录。 默认值: false
    • verbatimSymlinks <boolean>true 时,将跳过符号链接的路径解析。默认值: false
  • callback <Function>

异步地将整个目录结构从 src 复制到 dest,包括子目录和文件。

当将一个目录复制到另一个目录时,不支持 glob,并且行为类似于 cp dir1/ dir2/

fs.createReadStream(path[, options])#

options 可以包含 startend 值,用于读取文件中某个字节范围的数据,而不是整个文件。startend 都是包含的,并且从 0 开始计数,允许的值在 [0, Number.MAX_SAFE_INTEGER] 范围内。如果指定了 fd 并且省略了 start 或为 undefined,则 fs.createReadStream() 从当前文件位置开始顺序读取。encoding 可以是 <Buffer> 接受的任何一种。

如果指定了 fd,则 ReadStream 将忽略 path 参数,并使用指定的文件描述符。这意味着不会触发 'open' 事件。 fd 应该是阻塞的;非阻塞的 fd 应该传递给 <net.Socket>

如果 fd 指向一个只支持阻塞读取的字符设备(如键盘或声卡),则读取操作只有在有数据可用时才会完成。 这可能会阻止进程退出,并阻止流自然关闭。

默认情况下,流在销毁后会发出 'close' 事件。 将 emitClose 选项设置为 false 以更改此行为。

通过提供 fs 选项,可以覆盖相应的 fs 实现,用于 openreadclose。 当提供 fs 选项时,必须覆盖 read。 如果未提供 fd,则还需要覆盖 open。 如果 autoClosetrue,则还需要覆盖 close

import { createReadStream } from 'node:fs';

// Create a stream from some character device.
const stream = createReadStream('/dev/input/event0');
setTimeout(() => {
  stream.close(); // This may not close the stream.
  // Artificially marking end-of-stream, as if the underlying resource had
  // indicated end-of-file by itself, allows the stream to close.
  // This does not cancel pending read operations, and if there is such an
  // operation, the process may still not be able to exit successfully
  // until it finishes.
  stream.push(null);
  stream.read(0);
}, 100); 

如果 autoClose 为 false,则即使出现错误,文件描述符也不会关闭。 应用程序有责任关闭它并确保没有文件描述符泄漏。 如果 autoClose 设置为 true(默认行为),则在 'error''end' 上,文件描述符将自动关闭。

mode 设置文件模式(权限和粘滞位),但仅当文件被创建时。

一个读取 100 字节长文件的最后 10 个字节的示例

import { createReadStream } from 'node:fs';

createReadStream('sample.txt', { start: 90, end: 99 }); 

如果 options 是一个字符串,那么它指定编码。

fs.createWriteStream(path[, options])#

options 还可以包含一个 start 选项,以允许在文件开头之后的某个位置写入数据,允许的值在 [0, Number.MAX_SAFE_INTEGER] 范围内。修改文件而不是替换它可能需要将 flags 选项设置为 r+,而不是默认的 wencoding 可以是 <Buffer> 接受的任何一种。

如果 autoClose'error''finish' 上设置为 true(默认行为),则文件描述符将自动关闭。 如果 autoClose 为 false,则即使出现错误,文件描述符也不会关闭。 应用程序有责任关闭它并确保没有文件描述符泄漏。

默认情况下,流在销毁后会发出 'close' 事件。 将 emitClose 选项设置为 false 以更改此行为。

通过提供 fs 选项,可以覆盖相应的 fs 实现,用于 openwritewritevclose。 如果没有 writev() 就覆盖 write() 可能会降低性能,因为某些优化 (_writev()) 将被禁用。 当提供 fs 选项时,必须至少覆盖 writewritev 之一。 如果未提供 fd 选项,则还需要覆盖 open。 如果 autoClosetrue,则还需要覆盖 close

<fs.ReadStream> 类似,如果指定了 fd,则 <fs.WriteStream> 将忽略 path 参数,并使用指定的文件描述符。这意味着不会触发 'open' 事件。 fd 应该是阻塞的;非阻塞的 fd 应该传递给 <net.Socket>

如果 options 是一个字符串,那么它指定编码。

fs.exists(path, callback)#

稳定性: 0 - 已弃用: 使用 fs.stat()fs.access() 代替。

通过检查文件系统,测试给定 path 上的元素是否存在。然后使用 true 或 false 调用 callback 参数

import { exists } from 'node:fs';

exists('/etc/passwd', (e) => {
  console.log(e ? 'it exists' : 'no passwd!');
}); 

此回调的参数与其他 Node.js 回调不一致。 通常,Node.js 回调的第一个参数是 err 参数,可以选择后跟其他参数。 fs.exists() 回调只有一个布尔参数。 这是建议使用 fs.access() 而不是 fs.exists() 的原因之一。

如果 path 是符号链接,它将被跟随。 因此,如果 path 存在但指向不存在的元素,则回调将收到值 false

不建议使用 fs.exists() 来检查文件是否存在,然后再调用 fs.open()fs.readFile()fs.writeFile()。 这样做会引入竞争条件,因为其他进程可能会在这两次调用之间更改文件的状态。 相反,用户代码应直接打开/读取/写入文件,并处理如果文件不存在时引发的错误。

写入(不推荐)

import { exists, open, close } from 'node:fs';

exists('myfile', (e) => {
  if (e) {
    console.error('myfile already exists');
  } else {
    open('myfile', 'wx', (err, fd) => {
      if (err) throw err;

      try {
        writeMyData(fd);
      } finally {
        close(fd, (err) => {
          if (err) throw err;
        });
      }
    });
  }
}); 

写入(推荐)

import { open, close } from 'node:fs';
open('myfile', 'wx', (err, fd) => {
  if (err) {
    if (err.code === 'EEXIST') {
      console.error('myfile already exists');
      return;
    }

    throw err;
  }

  try {
    writeMyData(fd);
  } finally {
    close(fd, (err) => {
      if (err) throw err;
    });
  }
}); 

读取(不推荐)

import { open, close, exists } from 'node:fs';

exists('myfile', (e) => {
  if (e) {
    open('myfile', 'r', (err, fd) => {
      if (err) throw err;

      try {
        readMyData(fd);
      } finally {
        close(fd, (err) => {
          if (err) throw err;
        });
      }
    });
  } else {
    console.error('myfile does not exist');
  }
}); 

读取(推荐)

import { open, close } from 'node:fs';

open('myfile', 'r', (err, fd) => {
  if (err) {
    if (err.code === 'ENOENT') {
      console.error('myfile does not exist');
      return;
    }

    throw err;
  }

  try {
    readMyData(fd);
  } finally {
    close(fd, (err) => {
      if (err) throw err;
    });
  }
}); 

上面“不推荐”的示例检查是否存在然后使用该文件; “推荐”的示例更好,因为它们直接使用该文件并处理错误(如果有)。

一般来说,只有在文件不会被直接使用的情况下,才检查文件是否存在,例如,当它的存在是来自另一个进程的信号时。

fs.fchmod(fd, mode, callback)#

设置文件的权限。 除了可能的异常之外,没有其他参数传递给完成回调。

有关更多详细信息,请参见 POSIX fchmod(2) 文档。

fs.fchown(fd, uid, gid, callback)#

设置文件的所有者。 除了可能的异常之外,没有其他参数传递给完成回调。

有关更多详细信息,请参见 POSIX fchown(2) 文档。

fs.fdatasync(fd, callback)#

强制将与文件关联的所有当前排队的 I/O 操作刷新到操作系统的同步 I/O 完成状态。 有关详细信息,请参阅 POSIX fdatasync(2) 文档。 除了可能的异常之外,没有其他参数传递给完成回调。

fs.fstat(fd[, options], callback)#

使用文件描述符的 <fs.Stats> 调用回调。

有关更多详细信息,请参见 POSIX fstat(2) 文档。

fs.fsync(fd, callback)#

请求将打开的文件描述符的所有数据刷新到存储设备。 具体实现是操作系统和设备特定的。 有关更多详细信息,请参阅 POSIX fsync(2) 文档。 除了可能的异常之外,没有其他参数传递给完成回调。

fs.ftruncate(fd[, len], callback)#

截断文件描述符。 除了可能的异常之外,没有其他参数传递给完成回调。

有关更多详细信息,请参见 POSIX ftruncate(2) 文档。

如果文件描述符引用的文件大于 len 字节,则只有前 len 个字节将保留在该文件中。

例如,以下程序仅保留文件的前四个字节

import { open, close, ftruncate } from 'node:fs';

function closeFd(fd) {
  close(fd, (err) => {
    if (err) throw err;
  });
}

open('temp.txt', 'r+', (err, fd) => {
  if (err) throw err;

  try {
    ftruncate(fd, 4, (err) => {
      closeFd(fd);
      if (err) throw err;
    });
  } catch (err) {
    closeFd(fd);
    if (err) throw err;
  }
}); 

如果文件之前短于 len 字节,则将其扩展,并且扩展的部分用空字节 ('\0') 填充

如果 len 为负数,则将使用 0

fs.futimes(fd, atime, mtime, callback)#

更改由提供的文件描述符引用的对象的文件系统时间戳。 参见 fs.utimes()

fs.glob(pattern[, options], callback)#

  • pattern <string> | <string[]>

  • options <Object>

    • cwd <string> 当前工作目录。 默认值: process.cwd()
    • exclude <Function> | <string[]> 用于过滤掉文件/目录的函数或要排除的 glob 模式列表。 如果提供一个函数,返回 true 以排除该项,返回 false 以包含该项。 默认值: undefined
    • withFileTypes <boolean> 如果 glob 应该将路径作为 Dirent 返回,则为 true,否则为 false默认值: false
  • callback <Function>

  • 检索与指定模式匹配的文件。

import { glob } from 'node:fs';

glob('**/*.js', (err, matches) => {
  if (err) throw err;
  console.log(matches);
});const { glob } = require('node:fs');

glob('**/*.js', (err, matches) => {
  if (err) throw err;
  console.log(matches);
});

fs.lchmod(path, mode, callback)#

稳定性: 0 - 已弃用

更改符号链接的权限。 除了可能的异常之外,没有其他参数传递给完成回调。

此方法仅在 macOS 上实现。

有关更多详细信息,请参见 POSIX lchmod(2) 文档。

fs.lchown(path, uid, gid, callback)#

设置符号链接的所有者。 除了可能的异常之外,没有其他参数传递给完成回调。

有关更多详细信息,请参见 POSIX lchown(2) 文档。

fs.lutimes(path, atime, mtime, callback)#

以与 fs.utimes() 相同的方式更改文件的访问时间和修改时间,不同之处在于,如果路径是指向符号链接,则不会取消对链接的引用:而是更改符号链接本身的的时间戳。

除了可能的异常之外,完成回调没有给出任何参数。

fs.link(existingPath, newPath, callback)#

existingPath 创建到 newPath 的新链接。 有关更多详细信息,请参见 POSIX link(2) 文档。 除了可能的异常之外,完成回调没有给出任何参数。

fs.lstat(path[, options], callback)#

检索路径所引用的符号链接的 <fs.Stats>。 回调获取两个参数 (err, stats),其中 stats<fs.Stats> 对象。 lstat()stat() 相同,除了如果 path 是符号链接,则对链接本身进行 stat,而不是对其引用的文件进行 stat。

有关更多详细信息,请参见 POSIX lstat(2) 文档。

fs.mkdir(path[, options], callback)#

异步地创建一个目录。

回调会得到一个可能的异常,如果 recursivetrue,则会得到第一个创建的目录路径,(err[, path])。 如果未创建任何目录(例如,如果先前已创建),则当 recursivetrue 时,path 仍然可以是 undefined

可选的 options 参数可以是一个整数,用于指定 mode(权限和粘滞位),也可以是一个具有 mode 属性和 recursive 属性的对象,该属性指示是否应创建父目录。 当 path 是现有目录时,调用 fs.mkdir() 仅当 recursive 为 false 时才会导致错误。 如果 recursive 为 false 且目录存在,则会发生 EEXIST 错误。

import { mkdir } from 'node:fs';

// Create ./tmp/a/apple, regardless of whether ./tmp and ./tmp/a exist.
mkdir('./tmp/a/apple', { recursive: true }, (err) => {
  if (err) throw err;
}); 

在 Windows 上,即使使用递归,在根目录上使用 fs.mkdir() 也会导致错误

import { mkdir } from 'node:fs';

mkdir('/', { recursive: true }, (err) => {
  // => [Error: EPERM: operation not permitted, mkdir 'C:\']
}); 

有关更多详细信息,请参见 POSIX mkdir(2) 文档。

fs.mkdtemp(prefix[, options], callback)#

创建一个唯一的临时目录。

生成六个随机字符,附加在必需的 prefix 之后,以创建一个唯一的临时目录。 由于平台不一致,请避免在 prefix 中使用尾随 X 字符。 某些平台(尤其是 BSD)可以返回六个以上的随机字符,并用随机字符替换 prefix 中的尾随 X 字符。

创建的目录路径作为字符串传递给回调的第二个参数。

可选的 options 参数可以是一个指定编码的字符串,或者是一个具有指定要使用的字符编码的 encoding 属性的对象。

import { mkdtemp } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';

mkdtemp(join(tmpdir(), 'foo-'), (err, directory) => {
  if (err) throw err;
  console.log(directory);
  // Prints: /tmp/foo-itXde2 or C:\Users\...\AppData\Local\Temp\foo-itXde2
}); 

fs.mkdtemp() 方法会将六个随机选择的字符直接附加到 prefix 字符串。 例如,给定一个目录 /tmp,如果打算在 /tmp创建一个临时目录,则 prefix 必须以特定于平台的尾随路径分隔符结尾(require('node:path').sep)。

import { tmpdir } from 'node:os';
import { mkdtemp } from 'node:fs';

// The parent directory for the new temporary directory
const tmpDir = tmpdir();

// This method is *INCORRECT*:
mkdtemp(tmpDir, (err, directory) => {
  if (err) throw err;
  console.log(directory);
  // Will print something similar to `/tmpabc123`.
  // A new temporary directory is created at the file system root
  // rather than *within* the /tmp directory.
});

// This method is *CORRECT*:
import { sep } from 'node:path';
mkdtemp(`${tmpDir}${sep}`, (err, directory) => {
  if (err) throw err;
  console.log(directory);
  // Will print something similar to `/tmp/abc123`.
  // A new temporary directory is created within
  // the /tmp directory.
}); 

fs.open(path[, flags[, mode]], callback)#

异步文件打开。 有关更多详细信息,请参见 POSIX open(2) 文档。

mode 设置文件模式(权限和粘滞位),但仅当创建文件时。 在 Windows 上,只能操作写入权限;请参阅 fs.chmod()

回调获取两个参数 (err, fd)

某些字符 (< > : " / \ | ? *) 在 Windows 下保留,如命名文件、路径和命名空间所述。 在 NTFS 下,如果文件名包含冒号,则 Node.js 将打开文件系统流,如此 MSDN 页面所述。

基于 fs.open() 的函数也表现出这种行为:fs.writeFile()fs.readFile() 等。

fs.openAsBlob(path[, options])#

返回一个 <Blob>,其数据由给定文件支持。

在创建 <Blob> 后,不得修改该文件。 任何修改都会导致读取 <Blob> 数据失败,并显示 DOMException 错误。 在创建 Blob 时以及在每次读取之前,对文件执行同步 stat 操作,以便检测文件数据是否已在磁盘上修改。

import { openAsBlob } from 'node:fs';

const blob = await openAsBlob('the.file.txt');
const ab = await blob.arrayBuffer();
blob.stream();const { openAsBlob } = require('node:fs');

(async () => {
  const blob = await openAsBlob('the.file.txt');
  const ab = await blob.arrayBuffer();
  blob.stream();
})();

fs.opendir(path[, options], callback)#

异步打开一个目录。 有关更多详细信息,请参见 POSIX opendir(3) 文档。

创建一个 <fs.Dir>,其中包含用于从目录读取和清理目录的所有其他函数。

encoding 选项设置打开目录和后续读取操作时 path 的编码。

fs.read(fd, buffer, offset, length, position, callback)#

fd 指定的文件读取数据。

回调函数有三个参数 (err, bytesRead, buffer)

如果文件没有被并发修改,当读取的字节数为零时,达到文件末尾。

如果此方法被调用为其 util.promisify()ed 版本,它会返回一个 Object 的 promise,该 Object 具有 bytesReadbuffer 属性。

fs.read() 方法从文件描述符 (fd) 指定的文件中读取数据。 length 参数指示 Node.js 将尝试从内核读取的最大字节数。 但是,由于各种原因,实际读取的字节数 (bytesRead) 可能低于指定的 length

例如

  • 如果文件短于指定的 length,则 bytesRead 将设置为实际读取的字节数。
  • 如果在缓冲区可以被填满之前文件遇到 EOF(文件结束),Node.js 将读取所有可用字节,直到遇到 EOF,并且回调中的 bytesRead 参数将指示实际读取的字节数,这可能小于指定的 length
  • 如果文件位于慢速网络 filesystem 上,或者在读取过程中遇到任何其他问题,则 bytesRead 可能低于指定的 length

因此,当使用 fs.read() 时,重要的是检查 bytesRead 值以确定实际从文件中读取了多少字节。 根据您的应用程序逻辑,您可能需要处理 bytesRead 低于指定 length 的情况,例如,如果您需要最小字节数,则将读取调用包装在循环中。

此行为类似于 POSIX preadv2 函数。

fs.read(fd[, options], callback)#

类似于 fs.read() 函数,此版本采用可选的 options 对象。 如果未指定 options 对象,它将默认为上述值。

fs.read(fd, buffer[, options], callback)#

类似于 fs.read() 函数,此版本采用可选的 options 对象。 如果未指定 options 对象,它将默认为上述值。

fs.readdir(path[, options], callback)#

读取目录的内容。 回调获取两个参数 (err, files),其中 files 是目录中文件名(不包括 '.''..')的数组。

有关更多详细信息,请参阅 POSIX readdir(3) 文档。

可选的 options 参数可以是一个字符串,用于指定编码,也可以是一个对象,其 encoding 属性用于指定用于传递给回调函数的文件名的字符编码。 如果将 encoding 设置为 'buffer',则返回的文件名将作为 <Buffer> 对象传递。

如果 options.withFileTypes 设置为 true,则 files 数组将包含 <fs.Dirent> 对象。

fs.readFile(path[, options], callback)#

异步读取文件的全部内容。

import { readFile } from 'node:fs';

readFile('/etc/passwd', (err, data) => {
  if (err) throw err;
  console.log(data);
}); 

回调传递两个参数 (err, data),其中 data 是文件的内容。

如果未指定编码,则返回原始缓冲区。

如果 options 是字符串,则它指定编码

import { readFile } from 'node:fs';

readFile('/etc/passwd', 'utf8', callback); 

当路径是一个目录时,fs.readFile()fs.readFileSync() 的行为是平台相关的。 在 macOS、Linux 和 Windows 上,将返回一个错误。 在 FreeBSD 上,将返回目录内容的表示形式。

import { readFile } from 'node:fs';

// macOS, Linux, and Windows
readFile('<directory>', (err, data) => {
  // => [Error: EISDIR: illegal operation on a directory, read <directory>]
});

//  FreeBSD
readFile('<directory>', (err, data) => {
  // => null, <data>
}); 

可以使用 AbortSignal 中止正在进行的请求。 如果请求被中止,则使用 AbortError 调用回调。

import { readFile } from 'node:fs';

const controller = new AbortController();
const signal = controller.signal;
readFile(fileInfo[0].name, { signal }, (err, buf) => {
  // ...
});
// When you want to abort the request
controller.abort(); 

fs.readFile() 函数缓冲整个文件。 为了最大限度地降低内存成本,在可能的情况下,首选通过 fs.createReadStream() 进行流式传输。

中止正在进行的请求不会中止单个操作系统请求,而是中止内部缓冲 fs.readFile 执行的。

文件描述符#
  1. 任何指定的文件描述符都必须支持读取。
  2. 如果将文件描述符指定为 path,则不会自动关闭它。
  3. 读取将从当前位置开始。 例如,如果文件已经有 'Hello World',并且使用文件描述符读取了六个字节,则使用相同的文件描述符调用 fs.readFile() 将给出 'World',而不是 'Hello World'
性能注意事项#

fs.readFile() 方法异步地将文件的内容一次一个块地读入内存,允许事件循环在每个块之间循环。 这允许读取操作对可能正在使用底层 libuv 线程池的其他活动产生较小的影响,但也意味着将花费更长的时间将完整的文件读入内存。

额外的读取开销在不同的系统上可能会有很大的差异,并且取决于正在读取的文件类型。 如果文件类型不是常规文件(例如管道),并且 Node.js 无法确定实际文件大小,则每次读取操作将加载 64 KiB 的数据。 对于常规文件,每次读取将处理 512 KiB 的数据。

对于需要尽可能快地读取文件内容的应用程序,最好直接使用 fs.read(),并由应用程序代码自行管理读取文件的完整内容。

Node.js GitHub 问题 #25741 提供了更多信息,并详细分析了 fs.readFile() 在不同 Node.js 版本中对多个文件大小的性能。

fs.readlink(path[, options], callback)#

读取 path 引用的符号链接的内容。 回调获取两个参数 (err, linkString)

有关更多详细信息,请参阅 POSIX readlink(2) 文档。

可选的 options 参数可以是一个字符串,用于指定编码,也可以是一个对象,其 encoding 属性用于指定用于传递给回调函数的链接路径的字符编码。 如果将 encoding 设置为 'buffer',则返回的链接路径将作为 <Buffer> 对象传递。

fs.readv(fd, buffers[, position], callback)#

fd 指定的文件中读取,并使用 readv() 写入到 ArrayBufferView 数组。

position 是应从中读取数据的文件的起始偏移量。 如果 typeof position !== 'number',则将从当前位置读取数据。

回调将获得三个参数:errbytesReadbuffersbytesRead 是从文件中读取的字节数。

如果此方法作为其 util.promisify() 版本调用,它将返回一个带有 bytesReadbuffers 属性的 Object 的 Promise。

fs.realpath(path[, options], callback)#

通过解析 ... 和符号链接异步计算规范路径名。

规范路径名不一定是唯一的。 硬链接和绑定挂载可以通过多个路径名公开文件系统实体。

此函数行为类似于 realpath(3),但有一些例外

  1. 在不区分大小写的文件系统上不执行大小写转换。

  2. 符号链接的最大数量与平台无关,并且通常(远)高于本机 realpath(3) 实现支持的数量。

callback 获取两个参数 (err, resolvedPath)。 可以使用 process.cwd 来解析相对路径。

仅支持可以转换为 UTF8 字符串的路径。

可选的 options 参数可以是一个字符串,用于指定编码,也可以是一个对象,其 encoding 属性用于指定用于传递给回调函数的路径的字符编码。 如果将 encoding 设置为 'buffer',则返回的路径将作为 <Buffer> 对象传递。

如果 path 解析为套接字或管道,则该函数将返回该对象的系统相关名称。

不存在的路径会导致 ENOENT 错误。 error.path 是绝对文件路径。

fs.realpath.native(path[, options], callback)#

异步 realpath(3)

callback 获取两个参数 (err, resolvedPath)

仅支持可以转换为 UTF8 字符串的路径。

可选的 options 参数可以是一个字符串,用于指定编码,也可以是一个对象,其 encoding 属性用于指定用于传递给回调函数的路径的字符编码。 如果将 encoding 设置为 'buffer',则返回的路径将作为 <Buffer> 对象传递。

在 Linux 上,当 Node.js 链接到 musl libc 时,必须将 procfs 文件系统挂载到 /proc 才能使此函数工作。 Glibc 没有此限制。

fs.rename(oldPath, newPath, callback)#

异步地将 oldPath 处的文件重命名为作为 newPath 提供的路径名。 如果 newPath 已经存在,它将被覆盖。 如果 newPath 处存在目录,则会引发错误。 除了可能的异常之外,不会向完成回调提供任何参数。

另见: rename(2)

import { rename } from 'node:fs';

rename('oldFile.txt', 'newFile.txt', (err) => {
  if (err) throw err;
  console.log('Rename complete!');
}); 

fs.rmdir(path[, options], callback)#

  • path <string> | <Buffer> | <URL>
  • options <Object>
    • maxRetries <integer> 如果遇到 EBUSYEMFILEENFILEENOTEMPTYEPERM 错误,Node.js 会重试该操作,并在每次尝试时线性退避等待 retryDelay 毫秒。 此选项表示重试次数。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 0
    • recursive <boolean> 如果为 true,则执行递归目录删除。 在递归模式下,操作会在失败时重试。 默认值: false已弃用。
    • retryDelay <integer> 重试之间等待的时间(以毫秒为单位)。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 100
  • callback <Function>

异步 rmdir(2)。 除了可能的异常之外,不会向完成回调提供任何参数。

在文件(而不是目录)上使用 fs.rmdir() 会导致 Windows 上的 ENOENT 错误和 POSIX 上的 ENOTDIR 错误。

要获得类似于 rm -rf Unix 命令的行为,请使用带有选项 { recursive: true, force: true }fs.rm()

fs.rm(path[, options], callback)#

  • path <string> | <Buffer> | <URL>
  • options <Object>
    • force <boolean>true 时,如果 path 不存在,则忽略异常。 默认值: false
    • maxRetries <integer> 如果遇到 EBUSYEMFILEENFILEENOTEMPTYEPERM 错误,Node.js 会重试该操作,并在每次尝试时线性退避等待 retryDelay 毫秒。 此选项表示重试次数。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 0
    • recursive <boolean> 如果 true,则执行递归删除。在递归模式下,操作失败后会重试。默认值: false
    • retryDelay <integer> 重试之间等待的时间(以毫秒为单位)。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 100
  • callback <Function>

异步删除文件和目录(以标准 POSIX rm 实用程序为模型)。除了可能的异常之外,不会向完成回调提供任何参数。

fs.stat(path[, options], callback)#

异步 stat(2)。回调函数接收两个参数 (err, stats),其中 stats 是一个 <fs.Stats> 对象。

如果发生错误,err.code 将是 常见系统错误之一。

fs.stat() 遵循符号链接。 使用 fs.lstat() 查看链接本身。

不建议使用 fs.stat() 在调用 fs.open()fs.readFile()fs.writeFile() 之前检查文件是否存在。 相反,用户代码应直接打开/读取/写入文件并处理在文件不可用时引发的错误。

要检查文件是否存在而不操作它,建议使用 fs.access()

例如,给定以下目录结构

- txtDir
-- file.txt
- app.js 

下一个程序将检查给定路径的统计信息

import { stat } from 'node:fs';

const pathsToCheck = ['./txtDir', './txtDir/file.txt'];

for (let i = 0; i < pathsToCheck.length; i++) {
  stat(pathsToCheck[i], (err, stats) => {
    console.log(stats.isDirectory());
    console.log(stats);
  });
} 

结果输出将类似于

true
Stats {
  dev: 16777220,
  mode: 16877,
  nlink: 3,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 14214262,
  size: 96,
  blocks: 0,
  atimeMs: 1561174653071.963,
  mtimeMs: 1561174614583.3518,
  ctimeMs: 1561174626623.5366,
  birthtimeMs: 1561174126937.2893,
  atime: 2019-06-22T03:37:33.072Z,
  mtime: 2019-06-22T03:36:54.583Z,
  ctime: 2019-06-22T03:37:06.624Z,
  birthtime: 2019-06-22T03:28:46.937Z
}
false
Stats {
  dev: 16777220,
  mode: 33188,
  nlink: 1,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 14214074,
  size: 8,
  blocks: 8,
  atimeMs: 1561174616618.8555,
  mtimeMs: 1561174614584,
  ctimeMs: 1561174614583.8145,
  birthtimeMs: 1561174007710.7478,
  atime: 2019-06-22T03:36:56.619Z,
  mtime: 2019-06-22T03:36:54.584Z,
  ctime: 2019-06-22T03:36:54.584Z,
  birthtime: 2019-06-22T03:26:47.711Z
} 

fs.statfs(path[, options], callback)#

异步 statfs(2)。 返回有关包含 path 的已挂载文件系统的信息。 回调函数接收两个参数 (err, stats),其中 stats 是一个 <fs.StatFs> 对象。

如果发生错误,err.code 将是 常见系统错误之一。

fs.symlink(target, path[, type], callback)#

创建名为 path 的链接,该链接指向 target。除了可能的异常之外,不会向完成回调提供任何参数。

有关更多详细信息,请参见 POSIX symlink(2) 文档。

type 参数仅在 Windows 上可用,而在其他平台上被忽略。 可以将其设置为 'dir''file''junction'。 如果 type 参数为 null,则 Node.js 将自动检测 target 类型并使用 'file''dir'。 如果 target 不存在,将使用 'file'。 Windows 连接点需要目标路径是绝对路径。 使用 'junction' 时,target 参数将自动规范化为绝对路径。 NTFS 卷上的连接点只能指向目录。

相对目标相对于链接的父目录。

import { symlink } from 'node:fs';

symlink('./mew', './mewtwo', callback); 

上面的示例创建一个指向同一目录中 mew 的符号链接 mewtwo

$ tree .
.
├── mew
└── mewtwo -> ./mew 

fs.truncate(path[, len], callback)#

截断文件。除了可能的异常之外,不会向完成回调提供任何参数。文件描述符也可以作为第一个参数传递。在这种情况下,将调用 fs.ftruncate()

import { truncate } from 'node:fs';
// Assuming that 'path/file.txt' is a regular file.
truncate('path/file.txt', (err) => {
  if (err) throw err;
  console.log('path/file.txt was truncated');
});const { truncate } = require('node:fs');
// Assuming that 'path/file.txt' is a regular file.
truncate('path/file.txt', (err) => {
  if (err) throw err;
  console.log('path/file.txt was truncated');
});

传递文件描述符已被弃用,将来可能会导致抛出错误。

有关更多详细信息,请参见 POSIX truncate(2) 文档。

fs.unlink(path, callback)#

异步删除文件或符号链接。除了可能的异常之外,不会向完成回调提供任何参数。

import { unlink } from 'node:fs';
// Assuming that 'path/file.txt' is a regular file.
unlink('path/file.txt', (err) => {
  if (err) throw err;
  console.log('path/file.txt was deleted');
}); 

fs.unlink() 将不适用于目录,无论是否为空。要删除目录,请使用 fs.rmdir()

有关更多详细信息,请参见 POSIX unlink(2) 文档。

fs.unwatchFile(filename[, listener])#

停止监视 filename 上的更改。如果指定了 listener,则仅删除该特定监听器。 否则,将删除所有监听器,从而有效地停止监视 filename

使用未被监视的文件名调用 fs.unwatchFile() 是一个空操作,而不是错误。

使用 fs.watch()fs.watchFile()fs.unwatchFile() 更有效。如果可能,应使用 fs.watch() 代替 fs.watchFile()fs.unwatchFile()

fs.utimes(path, atime, mtime, callback)#

更改 path 引用的对象的文件系统时间戳。

atimemtime 参数遵循以下规则

  • 值可以是表示 Unix 纪元时间的数字(以秒为单位)、Date 或类似 '123456789.0' 的数字字符串。
  • 如果该值无法转换为数字,或者为 NaNInfinity-Infinity,则会抛出 Error

fs.watch(filename[, options][, listener])#

监视 filename 上的更改,其中 filename 是文件或目录。

第二个参数是可选的。 如果 options 作为字符串提供,则它指定 encoding。 否则,应将 options 作为对象传递。

监听器回调函数接收两个参数 (eventType, filename)eventType'rename''change'filename 是触发该事件的文件名。

在大多数平台上,每当文件名在目录中出现或消失时,都会发出 'rename'

监听器回调函数附加到由 <fs.FSWatcher> 触发的 'change' 事件,但它与 eventType'change' 值不同。

如果传递了 signal,则中止相应的 AbortController 将关闭返回的 <fs.FSWatcher>

注意事项#

fs.watch API 在不同平台上并非 100% 一致,并且在某些情况下不可用。

在 Windows 上,如果监视的目录被移动或重命名,则不会发出任何事件。删除监视的目录时,将报告 EPERM 错误。

fs.watch API 不提供任何关于文件系统恶意行为的保护。 例如,在 Windows 上,它通过监视目录中的更改与特定文件相对比来实现。 这允许替换文件,并且 fs 报告对具有相同文件名的新文件的更改。

可用性#

此功能取决于底层操作系统提供一种通知文件系统更改的方式。

  • 在 Linux 系统上,它使用 inotify(7)
  • 在 BSD 系统上,它使用 kqueue(2)
  • 在 macOS 上,它对文件使用 kqueue(2),对目录使用 FSEvents
  • 在 SunOS 系统(包括 Solaris 和 SmartOS)上,它使用 event ports
  • 在 Windows 系统上,此功能取决于 ReadDirectoryChangesW
  • 在 AIX 系统上,此功能取决于 AHAFS,必须启用它。
  • 在 IBM i 系统上,不支持此功能。

如果底层功能由于某种原因不可用,则 fs.watch() 将无法运行,并且可能会引发异常。例如,在使用 Vagrant 或 Docker 等虚拟化软件时,监视网络文件系统(NFS、SMB 等)或主机文件系统上的文件或目录可能不可靠,并且在某些情况下是不可能的。

仍然可以使用 fs.watchFile(),它使用 stat 轮询,但是此方法速度较慢且可靠性较低。

Inodes#

在 Linux 和 macOS 系统上,fs.watch() 将路径解析为 inode 并监视该 inode。 如果监视的路径被删除并重新创建,则会为其分配一个新的 inode。 该监视将为删除发出一个事件,但将继续监视原始 inode。 将不会发出新 inode 的事件。 这是预期的行为。

AIX 文件在其生命周期内保留相同的 inode。 在 AIX 上保存和关闭受监视的文件将导致两个通知(一个用于添加新内容,一个用于截断)。

文件名参数#

回调中提供 filename 参数仅在 Linux、macOS、Windows 和 AIX 上受支持。即使在支持的平台上,也无法保证始终提供 filename。因此,不要假设回调中始终提供 filename 参数,如果它是 null,则需要一些回退逻辑。

import { watch } from 'node:fs';
watch('somedir', (eventType, filename) => {
  console.log(`event type is: ${eventType}`);
  if (filename) {
    console.log(`filename provided: ${filename}`);
  } else {
    console.log('filename not provided');
  }
}); 

fs.watchFile(filename[, options], listener)#

监视 filename 上的更改。 每次访问文件时都会调用回调 listener

可以省略 options 参数。 如果提供,它应该是一个对象。 options 对象可能包含一个名为 persistent 的布尔值,指示只要文件正在被监视,该进程是否应该继续运行。 options 对象可以指定一个 interval 属性,指示目标应该以毫秒为单位轮询的频率。

listener 得到两个参数,当前 stat 对象和之前的 stat 对象

import { watchFile } from 'node:fs';

watchFile('message.text', (curr, prev) => {
  console.log(`the current mtime is: ${curr.mtime}`);
  console.log(`the previous mtime was: ${prev.mtime}`);
}); 

这些 stat 对象是 fs.Stat 的实例。 如果 bigint 选项为 true,则这些对象中的数值将指定为 BigInt

要获得文件被修改的通知,而不仅仅是被访问的通知,有必要比较 curr.mtimeMsprev.mtimeMs

fs.watchFile 操作导致 ENOENT 错误时,它将调用一次监听器,所有字段都归零(或者对于日期,为 Unix Epoch)。 如果文件稍后创建,则将再次调用监听器,并使用最新的 stat 对象。 这是自 v0.10 以来功能上的变化。

使用 fs.watch()fs.watchFilefs.unwatchFile 更有效。 如果可能,应使用 fs.watch 代替 fs.watchFilefs.unwatchFile

fs.watchFile() 监视的文件消失并重新出现时,第二个回调事件(文件重新出现)中 previous 的内容将与第一个回调事件(文件消失)中 previous 的内容相同。

当发生以下情况时,会发生这种情况

  • 文件被删除,然后恢复
  • 文件被重命名,然后第二次重命名回其原始名称

fs.write(fd, buffer, offset[, length[, position]], callback)#

buffer 写入由 fd 指定的文件。

offset 确定要写入的缓冲区部分,而 length 是一个整数,指定要写入的字节数。

position 是指从文件开头到应写入此数据的偏移量。 如果 typeof position !== 'number',则数据将写入当前位置。 参见 pwrite(2)

该回调将获得三个参数 (err, bytesWritten, buffer),其中 bytesWritten 指定从 buffer 中写入了多少字节

如果此方法被调用作为它的 util.promisify()ed 版本,它返回一个 Object 的 promise,包含 bytesWrittenbuffer 属性。

在没有等待回调的情况下,在同一文件上多次使用 fs.write() 是不安全的。 对于这种情况,建议使用 fs.createWriteStream()

在 Linux 上,当文件以追加模式打开时,位置写入不起作用。 内核忽略位置参数,总是将数据追加到文件末尾。

fs.write(fd, buffer[, options], callback)#

buffer 写入由 fd 指定的文件。

与上面的 fs.write 函数类似,此版本采用一个可选的 options 对象。 如果未指定 options 对象,则它将默认为上述值。

fs.write(fd, string[, position[, encoding]], callback)#

string 写入由 fd 指定的文件。 如果 string 不是字符串,则会引发异常。

position 是指从文件开头到应写入此数据的偏移量。 如果 typeof position !== 'number',则数据将写入当前位置。 参见 pwrite(2)

encoding 是预期的字符串编码。

回调将接收参数 (err, written, string),其中 written 指定传递的字符串需要写入多少字节。 写入的字节不一定与写入的字符串字符相同。 参见 Buffer.byteLength

在没有等待回调的情况下,在同一文件上多次使用 fs.write() 是不安全的。 对于这种情况,建议使用 fs.createWriteStream()

在 Linux 上,当文件以追加模式打开时,位置写入不起作用。 内核忽略位置参数,总是将数据追加到文件末尾。

在 Windows 上,如果文件描述符连接到控制台(例如,fd == 1stdout),则默认情况下包含非 ASCII 字符的字符串将无法正确呈现,无论使用何种编码。 可以通过使用 chcp 65001 命令更改活动代码页来配置控制台以正确呈现 UTF-8。 有关更多详细信息,请参见 chcp 文档。

fs.writeFile(file, data[, options], callback)#

file 是文件名时,异步地将数据写入文件,如果文件已存在,则替换该文件。 data 可以是字符串或缓冲区。

file 是文件描述符时,该行为类似于直接调用 fs.write()(建议这样做)。 请参见下面有关使用文件描述符的说明。

如果 data 是 buffer,则忽略 encoding 选项。

mode 选项仅影响新创建的文件。 有关更多详细信息,请参见 fs.open()

import { writeFile } from 'node:fs';
import { Buffer } from 'node:buffer';

const data = new Uint8Array(Buffer.from('Hello Node.js'));
writeFile('message.txt', data, (err) => {
  if (err) throw err;
  console.log('The file has been saved!');
}); 

如果 options 是字符串,则它指定编码

import { writeFile } from 'node:fs';

writeFile('message.txt', 'Hello Node.js', 'utf8', callback); 

在没有等待回调的情况下,在同一文件上多次使用 fs.writeFile() 是不安全的。 对于这种情况,建议使用 fs.createWriteStream()

fs.readFile 类似 - fs.writeFile 是一种方便的方法,它在内部执行多个 write 调用以写入传递给它的缓冲区。 对于性能敏感的代码,请考虑使用 fs.createWriteStream()

可以使用 <AbortSignal> 来取消 fs.writeFile()。 取消是“尽力而为”,并且可能仍会写入一定数量的数据。

import { writeFile } from 'node:fs';
import { Buffer } from 'node:buffer';

const controller = new AbortController();
const { signal } = controller;
const data = new Uint8Array(Buffer.from('Hello Node.js'));
writeFile('message.txt', data, { signal }, (err) => {
  // When a request is aborted - the callback is called with an AbortError
});
// When the request should be aborted
controller.abort(); 

中止正在进行的请求不会中止单个操作系统请求,而是中止 fs.writeFile 执行的内部缓冲。

fs.writeFile() 与文件描述符一起使用#

file 是文件描述符时,该行为几乎与直接调用 fs.write() 相同,例如

import { write } from 'node:fs';
import { Buffer } from 'node:buffer';

write(fd, Buffer.from(data, options.encoding), callback); 

与直接调用 fs.write() 的区别在于,在某些不寻常的情况下,fs.write() 可能仅写入缓冲区的一部分,并且需要重试才能写入剩余数据,而 fs.writeFile() 会重试直到数据完全写入(或发生错误)。

这其中蕴含的含义是造成困惑的常见原因。 在文件描述符的情况下,文件不会被替换! 数据不一定写入文件的开头,并且文件中的原始数据可能保留在新写入数据之前和/或之后。

例如,如果连续两次调用 fs.writeFile(),首先写入字符串 'Hello',然后写入字符串 ', World',则该文件将包含 'Hello, World',并且可能包含文件的一些原始数据(取决于原始文件的大小以及文件描述符的位置)。 如果使用文件名而不是描述符,则可以保证该文件仅包含 ', World'

fs.writev(fd, buffers[, position], callback)#

使用 writev()ArrayBufferView 的数组写入由 fd 指定的文件。

position 是指从文件开头到应写入此数据的偏移量。 如果 typeof position !== 'number',则数据将写入当前位置。

回调将获得三个参数:errbytesWrittenbuffersbytesWritten 是从 buffers 写入的字节数。

如果此方法是 util.promisify()ed,它返回一个 Object 的 promise,包含 bytesWrittenbuffers 属性。

在没有等待回调的情况下,在同一文件上多次使用 fs.writev() 是不安全的。 对于这种情况,请使用 fs.createWriteStream()

在 Linux 上,当文件以追加模式打开时,位置写入不起作用。 内核忽略位置参数,总是将数据追加到文件末尾。

同步 API#

同步 API 同步地执行所有操作,阻止事件循环,直到操作完成或失败。

fs.accessSync(path[, mode])#

同步测试用户对 path 指定的文件或目录的权限。 mode 参数是一个可选的整数,用于指定要执行的可访问性检查。 mode 应该是值 fs.constants.F_OK,或者是由 fs.constants.R_OKfs.constants.W_OKfs.constants.X_OK 进行按位或运算组成的掩码(例如 fs.constants.W_OK | fs.constants.R_OK)。 查看 文件访问常量 以获取 mode 的可能值。

如果任何可访问性检查失败,将抛出一个 Error。 否则,该方法将返回 undefined

import { accessSync, constants } from 'node:fs';

try {
  accessSync('etc/passwd', constants.R_OK | constants.W_OK);
  console.log('can read/write');
} catch (err) {
  console.error('no access!');
} 

fs.appendFileSync(path, data[, options])#

同步地将数据追加到文件中,如果文件尚不存在,则创建该文件。 data 可以是字符串或 <Buffer>

mode 选项仅影响新创建的文件。 有关更多详细信息,请参见 fs.open()

import { appendFileSync } from 'node:fs';

try {
  appendFileSync('message.txt', 'data to append');
  console.log('The "data to append" was appended to file!');
} catch (err) {
  /* Handle the error */
} 

如果 options 是字符串,则它指定编码

import { appendFileSync } from 'node:fs';

appendFileSync('message.txt', 'data to append', 'utf8'); 

path 可以指定为已打开用于附加的数字文件描述符(使用 fs.open()fs.openSync())。 文件描述符不会自动关闭。

import { openSync, closeSync, appendFileSync } from 'node:fs';

let fd;

try {
  fd = openSync('message.txt', 'a');
  appendFileSync(fd, 'data to append', 'utf8');
} catch (err) {
  /* Handle the error */
} finally {
  if (fd !== undefined)
    closeSync(fd);
} 

fs.chmodSync(path, mode)#

有关详细信息,请参阅此 API 的异步版本的文档:fs.chmod()

有关更多详细信息,请参阅 POSIX chmod(2) 文档。

fs.chownSync(path, uid, gid)#

同步地更改文件的所有者和群组。 返回 undefined。 这是 fs.chown() 的同步版本。

有关更多详细信息,请参阅 POSIX chown(2) 文档。

fs.closeSync(fd)#

关闭文件描述符。 返回 undefined

通过任何其他 fs 操作在当前使用的任何文件描述符 (fd) 上调用 fs.closeSync() 可能会导致未定义的行为。

有关更多详细信息,请参阅 POSIX close(2) 文档。

fs.copyFileSync(src, dest[, mode])#

同步地将 src 复制到 dest。 默认情况下,如果 dest 已经存在,则会覆盖它。 返回 undefined。 Node.js 不保证复制操作的原子性。 如果在目标文件已打开以进行写入后发生错误,Node.js 将尝试删除目标。

mode 是一个可选的整数,用于指定复制操作的行为。 可以创建一个由两个或多个值的按位或组成的掩码(例如 fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE)。

  • fs.constants.COPYFILE_EXCL:如果 dest 已经存在,复制操作将失败。
  • fs.constants.COPYFILE_FICLONE:复制操作将尝试创建一个写时复制的 reflink。如果平台不支持写时复制,则使用备用复制机制。
  • fs.constants.COPYFILE_FICLONE_FORCE:复制操作将尝试创建一个写时复制的 reflink。如果平台不支持写时复制,则操作将失败。
import { copyFileSync, constants } from 'node:fs';

// destination.txt will be created or overwritten by default.
copyFileSync('source.txt', 'destination.txt');
console.log('source.txt was copied to destination.txt');

// By using COPYFILE_EXCL, the operation will fail if destination.txt exists.
copyFileSync('source.txt', 'destination.txt', constants.COPYFILE_EXCL); 

fs.cpSync(src, dest[, options])#

  • src <string> | <URL> 要复制的源路径。
  • dest <string> | <URL> 要复制到的目标路径。
  • options <Object>
    • dereference <boolean> 解引用符号链接。 默认值: false
    • errorOnExist <boolean>forcefalse 并且目标存在时,抛出一个错误。 默认值: false
    • filter <Function> 用于过滤复制的文件/目录的函数。 返回 true 以复制该项,返回 false 以忽略它。 忽略目录时,也会跳过其所有内容。 默认: undefined
      • src <string> 要复制的源路径。
      • dest <string> 要复制到的目标路径。
      • 返回: <boolean> 任何可以强制转换为 boolean 的非 Promise 值。
    • force <boolean> 覆盖现有文件或目录。如果将此选项设置为 false 且目标存在,则复制操作将忽略错误。使用 errorOnExist 选项来更改此行为。默认值: true
    • mode <integer> 复制操作的修饰符。 默认: 0。 参见 fs.copyFileSync()mode 标志。
    • preserveTimestamps <boolean>true 时,将保留来自 src 的时间戳。默认值: false
    • recursive <boolean> 递归复制目录。 默认值: false
    • verbatimSymlinks <boolean>true 时,将跳过符号链接的路径解析。默认值: false

同步地将整个目录结构从 src 复制到 dest,包括子目录和文件。

当将一个目录复制到另一个目录时,不支持 glob,并且行为类似于 cp dir1/ dir2/

fs.existsSync(path)#

如果路径存在,则返回 true,否则返回 false

有关详细信息,请参阅此 API 的异步版本的文档:fs.exists()

fs.exists() 已弃用,但 fs.existsSync() 没有。 fs.exists()callback 参数接受与其他 Node.js 回调不一致的参数。 fs.existsSync() 不使用回调。

import { existsSync } from 'node:fs';

if (existsSync('/etc/passwd'))
  console.log('The path exists.'); 

fs.fchmodSync(fd, mode)#

设置文件上的权限。 返回 undefined

有关更多详细信息,请参见 POSIX fchmod(2) 文档。

fs.fchownSync(fd, uid, gid)#

设置文件的所有者。 返回 undefined

有关更多详细信息,请参见 POSIX fchown(2) 文档。

fs.fdatasyncSync(fd)#

强制将与文件关联的所有当前排队的 I/O 操作置于操作系统同步的 I/O 完成状态。 有关详细信息,请参阅 POSIX fdatasync(2) 文档。 返回 undefined

fs.fstatSync(fd[, options])#

检索文件描述符的 <fs.Stats>

有关更多详细信息,请参见 POSIX fstat(2) 文档。

fs.fsyncSync(fd)#

请求将打开的文件描述符的所有数据刷新到存储设备。 具体实现取决于操作系统和设备。 有关更多详细信息,请参阅 POSIX fsync(2) 文档。 返回 undefined

fs.ftruncateSync(fd[, len])#

截断文件描述符。 返回 undefined

有关详细信息,请参阅此 API 的异步版本的文档:fs.ftruncate()

fs.futimesSync(fd, atime, mtime)#

fs.futimes() 的同步版本。 返回 undefined

fs.globSync(pattern[, options])#

  • pattern <string> | <string[]>
  • options <Object>
    • cwd <string> 当前工作目录。 默认值: process.cwd()
    • exclude <Function> | <string[]> 用于过滤掉文件/目录的函数或要排除的 glob 模式列表。 如果提供一个函数,返回 true 以排除该项,返回 false 以包含该项。 默认值: undefined
    • withFileTypes <boolean> 如果 glob 应该将路径作为 Dirent 返回,则为 true,否则为 false默认值: false
  • 返回: <string[]> 与模式匹配的文件的路径。
import { globSync } from 'node:fs';

console.log(globSync('**/*.js'));const { globSync } = require('node:fs');

console.log(globSync('**/*.js'));

fs.lchmodSync(path, mode)#

稳定性: 0 - 已弃用

更改符号链接上的权限。 返回 undefined

此方法仅在 macOS 上实现。

有关更多详细信息,请参见 POSIX lchmod(2) 文档。

fs.lchownSync(path, uid, gid)#

为路径设置所有者。 返回 undefined

有关更多详细信息,请参阅 POSIX lchown(2) 文档。

fs.lutimesSync(path, atime, mtime)#

更改由 path 引用的符号链接的文件系统时间戳。 返回 undefined,或者当参数不正确或操作失败时抛出异常。 这是 fs.lutimes() 的同步版本。

fs.linkSync(existingPath, newPath)#

existingPathnewPath 创建一个新链接。 有关更多详细信息,请参阅 POSIX link(2) 文档。 返回 undefined

fs.lstatSync(path[, options])#

检索由 path 引用的符号链接的 <fs.Stats>

有关更多详细信息,请参见 POSIX lstat(2) 文档。

fs.mkdirSync(path[, options])#

同步地创建一个目录。 返回 undefined,或者如果 recursivetrue,则返回创建的第一个目录路径。 这是 fs.mkdir() 的同步版本。

有关更多详细信息,请参见 POSIX mkdir(2) 文档。

fs.mkdtempSync(prefix[, options])#

返回创建的目录路径。

有关详细信息,请参阅此 API 的异步版本的文档:fs.mkdtemp()

可选的 options 参数可以是一个指定编码的字符串,或者是一个具有指定要使用的字符编码的 encoding 属性的对象。

fs.opendirSync(path[, options])#

同步地打开一个目录。 参见 opendir(3)

创建一个 <fs.Dir>,其中包含用于从目录读取和清理目录的所有其他函数。

encoding 选项设置打开目录和后续读取操作时 path 的编码。

fs.openSync(path[, flags[, mode]])#

返回一个表示文件描述符的整数。

有关详细信息,请参阅此 API 的异步版本的文档:fs.open()

fs.readdirSync(path[, options])#

读取目录的内容。

有关更多详细信息,请参阅 POSIX readdir(3) 文档。

可选的 options 参数可以是一个指定编码的字符串,或者是一个带有 encoding 属性的对象,用于指定返回的文件名所使用的字符编码。 如果 encoding 设置为 'buffer',则返回的文件名将作为 <Buffer> 对象传递。

如果 options.withFileTypes 设置为 true,则结果将包含 <fs.Dirent> 对象。

fs.readFileSync(path[, options])#

返回 path 的内容。

有关详细信息,请参阅此 API 的异步版本的文档:fs.readFile()

如果指定了 encoding 选项,则此函数返回一个字符串。 否则,它返回一个缓冲区。

类似于 fs.readFile(),当路径是一个目录时,fs.readFileSync() 的行为是特定于平台的。

import { readFileSync } from 'node:fs';

// macOS, Linux, and Windows
readFileSync('<directory>');
// => [Error: EISDIR: illegal operation on a directory, read <directory>]

//  FreeBSD
readFileSync('<directory>'); // => <data> 

fs.readlinkSync(path[, options])#

返回符号链接的字符串值。

有关更多详细信息,请参阅 POSIX readlink(2) 文档。

可选的 options 参数可以是一个指定编码的字符串,或者是一个具有 encoding 属性的对象,该属性指定用于返回的链接路径的字符编码。 如果 encoding 设置为 'buffer',则返回的链接路径将作为 <Buffer> 对象传递。

fs.readSync(fd, buffer, offset, length[, position])#

返回 bytesRead 的数量。

有关详细信息,请参阅此 API 的异步版本的文档:fs.read()

fs.readSync(fd, buffer[, options])#

返回 bytesRead 的数量。

与上面的 fs.readSync 函数类似,此版本接受一个可选的 options 对象。 如果未指定 options 对象,它将默认使用上述值。

有关详细信息,请参阅此 API 的异步版本的文档:fs.read()

fs.readvSync(fd, buffers[, position])#

有关详细信息,请参阅此 API 的异步版本的文档:fs.readv()

fs.realpathSync(path[, options])#

返回已解析的路径名。

有关详细信息,请参阅此 API 的异步版本的文档:fs.realpath()

fs.realpathSync.native(path[, options])#

同步的 realpath(3)

仅支持可以转换为 UTF8 字符串的路径。

可选的 options 参数可以是一个指定编码的字符串,或者是一个带有 encoding 属性的对象,用于指定返回的路径所使用的字符编码。 如果 encoding 设置为 'buffer',则返回的路径将作为 <Buffer> 对象传递。

在 Linux 上,当 Node.js 链接到 musl libc 时,必须将 procfs 文件系统挂载到 /proc 才能使此函数工作。 Glibc 没有此限制。

fs.renameSync(oldPath, newPath)#

将文件从 oldPath 重命名为 newPath。 返回 undefined

有关更多详细信息,请参阅 POSIX rename(2) 文档。

fs.rmdirSync(path[, options])#

  • path <string> | <Buffer> | <URL>
  • options <Object>
    • maxRetries <integer> 如果遇到 EBUSYEMFILEENFILEENOTEMPTYEPERM 错误,Node.js 会重试该操作,并在每次尝试时线性退避等待 retryDelay 毫秒。 此选项表示重试次数。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 0
    • recursive <boolean> 如果为 true,则执行递归目录删除。 在递归模式下,操作会在失败时重试。 默认值: false已弃用。
    • retryDelay <integer> 重试之间等待的时间(以毫秒为单位)。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 100

同步的 rmdir(2)。 返回 undefined

在文件(不是目录)上使用 fs.rmdirSync() 会导致 Windows 上出现 ENOENT 错误,POSIX 上出现 ENOTDIR 错误。

要获得类似于 rm -rf Unix 命令的行为,请使用 fs.rmSync(),选项为 { recursive: true, force: true }

fs.rmSync(path[, options])#

  • path <string> | <Buffer> | <URL>
  • options <Object>
    • force <boolean>true 时,如果 path 不存在,则忽略异常。 默认值: false
    • maxRetries <integer> 如果遇到 EBUSYEMFILEENFILEENOTEMPTYEPERM 错误,Node.js 会重试该操作,并在每次尝试时线性退避等待 retryDelay 毫秒。 此选项表示重试次数。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 0
    • recursive <boolean> 如果为 true,则执行递归目录删除。 在递归模式下,操作会在失败时重试。 默认值: false
    • retryDelay <integer> 重试之间等待的时间(以毫秒为单位)。 如果 recursive 选项不是 true,则忽略此选项。 默认值: 100

同步删除文件和目录(以标准 POSIX rm 实用程序为模型)。 返回 undefined

fs.statSync(path[, options])#

检索路径的 <fs.Stats>

fs.statfsSync(path[, options])#

同步的 statfs(2)。 返回有关包含 path 的已挂载文件系统的信息。

如果发生错误,err.code 将是 常见系统错误之一。

fs.symlinkSync(target, path[, type])#

返回 undefined

有关详细信息,请参阅此 API 的异步版本的文档:fs.symlink()

fs.truncateSync(path[, len])#

截断文件。 返回 undefined。 文件描述符也可以作为第一个参数传递。 在这种情况下,将调用 fs.ftruncateSync()

传递文件描述符已被弃用,将来可能会导致抛出错误。

fs.unlinkSync(path)#

同步的 unlink(2)。 返回 undefined

fs.utimesSync(path, atime, mtime)#

返回 undefined

有关详细信息,请参阅此 API 的异步版本的文档:fs.utimes()

fs.writeFileSync(file, data[, options])#

返回 undefined

mode 选项仅影响新创建的文件。 有关更多详细信息,请参见 fs.open()

有关详细信息,请参阅此 API 的异步版本的文档:fs.writeFile()

fs.writeSync(fd, buffer, offset[, length[, position]])#

有关详细信息,请参阅此 API 的异步版本的文档:fs.write(fd, buffer...)

fs.writeSync(fd, buffer[, options])#

有关详细信息,请参阅此 API 的异步版本的文档:fs.write(fd, buffer...)

fs.writeSync(fd, string[, position[, encoding]])#

有关详细信息,请参阅此 API 的异步版本的文档:fs.write(fd, string...)

fs.writevSync(fd, buffers[, position])#

有关详细信息,请参阅此 API 的异步版本的文档:fs.writev()

常用对象#

常用对象由所有文件系统 API 变体(promise、回调和同步)共享。

类:fs.Dir#

表示目录流的类。

fs.opendir()fs.opendirSync()fsPromises.opendir() 创建。

import { opendir } from 'node:fs/promises';

try {
  const dir = await opendir('./');
  for await (const dirent of dir)
    console.log(dirent.name);
} catch (err) {
  console.error(err);
} 

当使用异步迭代器时,在迭代器退出后,<fs.Dir> 对象将自动关闭。

dir.close()#

异步关闭目录的底层资源句柄。 后续读取将导致错误。

返回一个 promise,该 promise 将在资源关闭后实现。

dir.close(callback)#

异步关闭目录的底层资源句柄。 后续读取将导致错误。

资源句柄关闭后,将调用 callback

dir.closeSync()#

同步关闭目录的底层资源句柄。 后续读取将导致错误。

dir.path#

此目录的只读路径,如提供给 fs.opendir()fs.opendirSync()fsPromises.opendir() 的路径。

dir.read()#

通过 readdir(3) 异步读取下一个目录条目作为 <fs.Dirent>

返回一个 promise,该 promise 将使用 <fs.Dirent> 实现,如果不再有目录条目要读取,则为 null

此函数返回的目录条目没有特定的顺序,由操作系统的底层目录机制提供。 在迭代目录时添加或删除的条目可能不包含在迭代结果中。

dir.read(callback)#

通过 readdir(3) 异步读取下一个目录条目作为 <fs.Dirent>

读取完成后,将使用 <fs.Dirent> 调用 callback,如果不再有目录条目要读取,则为 null

此函数返回的目录条目没有特定的顺序,由操作系统的底层目录机制提供。 在迭代目录时添加或删除的条目可能不包含在迭代结果中。

dir.readSync()#

同步读取下一个目录条目作为 <fs.Dirent>。 有关更多详细信息,请参阅 POSIX readdir(3) 文档。

如果没有任何更多目录条目可以读取,则将返回 null

此函数返回的目录条目没有特定的顺序,由操作系统的底层目录机制提供。 在迭代目录时添加或删除的条目可能不包含在迭代结果中。

dir[Symbol.asyncIterator]()#

异步遍历目录,直到读取所有条目。 有关更多详细信息,请参阅 POSIX readdir(3) 文档。

异步迭代器返回的条目始终是 <fs.Dirent>dir.read() 中的 null 情况在内部处理。

有关示例,请参见 <fs.Dir>

此迭代器返回的目录条目没有特定的顺序,这取决于操作系统底层目录机制的提供。 在迭代目录时添加或删除的条目可能不包括在迭代结果中。

类: fs.Dirent#

目录条目的表示形式,可以是目录中的文件或子目录,通过从 <fs.Dir> 读取返回。 目录条目是文件名和文件类型对的组合。

此外,当调用 fs.readdir()fs.readdirSync() 并且将 withFileTypes 选项设置为 true 时,生成的数组将填充 <fs.Dirent> 对象,而不是字符串或 <Buffer>

dirent.isBlockDevice()#

如果 <fs.Dirent> 对象描述的是块设备,则返回 true

dirent.isCharacterDevice()#

如果 <fs.Dirent> 对象描述的是字符设备,则返回 true

dirent.isDirectory()#

如果 <fs.Dirent> 对象描述的是文件系统目录,则返回 true

dirent.isFIFO()#

如果 <fs.Dirent> 对象描述的是先进先出 (FIFO) 管道,则返回 true

dirent.isFile()#

如果 <fs.Dirent> 对象描述的是常规文件,则返回 true

dirent.isSocket()#

如果 <fs.Dirent> 对象描述的是套接字,则返回 true

dirent.isSymbolicLink()#

如果 <fs.Dirent> 对象描述的是符号链接,则返回 true

dirent.name#

<fs.Dirent> 对象引用的文件名。 此值的类型由传递给 fs.readdir()fs.readdirSync()options.encoding 决定。

dirent.parentPath#

<fs.Dirent> 对象引用的文件的父目录的路径。

类: fs.FSWatcher#

成功调用 fs.watch() 方法将返回一个新的 <fs.FSWatcher> 对象。

当特定的被监视文件被修改时,所有的 <fs.FSWatcher> 对象都会触发一个 'change' 事件。

事件: 'change'#
  • eventType <string> 发生的更改事件的类型
  • filename <string> | <Buffer> 更改的文件名(如果相关/可用)

当被监视的目录或文件中的内容发生更改时,将触发此事件。 更多详细信息请参见 fs.watch()

filename 参数可能不会根据操作系统支持提供。 如果提供了 filename,则如果使用将其 encoding 选项设置为 'buffer' 调用 fs.watch(),则将 filename 作为 <Buffer> 提供,否则 filename 将是 UTF-8 字符串。

import { watch } from 'node:fs';
// Example when handled through fs.watch() listener
watch('./tmp', { encoding: 'buffer' }, (eventType, filename) => {
  if (filename) {
    console.log(filename);
    // Prints: <Buffer ...>
  }
}); 
事件: 'close'#

当监视器停止监视更改时触发。 关闭的 <fs.FSWatcher> 对象在事件处理程序中不再可用。

事件: 'error'#

在监视文件时发生错误时触发。 发生错误的 <fs.FSWatcher> 对象在事件处理程序中不再可用。

watcher.close()#

停止监视给定 <fs.FSWatcher> 上的更改。 停止后,<fs.FSWatcher> 对象不再可用。

watcher.ref()#

当调用时,请求 Node.js 事件循环<fs.FSWatcher> 处于活动状态时退出。 多次调用 watcher.ref() 不会有任何效果。

默认情况下,所有 <fs.FSWatcher> 对象都被 "ref'ed",因此通常不需要调用 watcher.ref(),除非之前调用过 watcher.unref()

watcher.unref()#

调用时,活动的 <fs.FSWatcher> 对象将不需要 Node.js 事件循环保持活动状态。 如果没有其他活动使事件循环保持运行,则进程可能会在调用 <fs.FSWatcher> 对象的回调之前退出。 多次调用 watcher.unref() 不会有任何效果。

类: fs.StatWatcher#

成功调用 fs.watchFile() 方法将返回一个新的 <fs.StatWatcher> 对象。

watcher.ref()#

当调用时,请求 Node.js 事件循环<fs.StatWatcher> 处于活动状态时退出。 多次调用 watcher.ref() 不会有任何效果。

默认情况下,所有 <fs.StatWatcher> 对象都被 "ref'ed",因此通常不需要调用 watcher.ref(),除非之前调用过 watcher.unref()

watcher.unref()#

调用时,活动的 <fs.StatWatcher> 对象将不需要 Node.js 事件循环保持活动状态。 如果没有其他活动使事件循环保持运行,则进程可能会在调用 <fs.StatWatcher> 对象的回调之前退出。 多次调用 watcher.unref() 不会有任何效果。

类: fs.ReadStream#

<fs.ReadStream> 的实例使用 fs.createReadStream() 函数创建并返回。

事件: 'close'#

<fs.ReadStream> 的底层文件描述符已关闭时,将触发此事件。

事件: 'open'#

<fs.ReadStream> 的文件描述符已打开时,将触发此事件。

事件: 'ready'#

<fs.ReadStream> 准备好使用时,将触发此事件。

'open' 之后立即触发。

readStream.bytesRead#

到目前为止已读取的字节数。

readStream.path#

流从中读取的文件路径,如 fs.createReadStream() 的第一个参数中所指定。 如果 path 作为字符串传递,则 readStream.path 将是一个字符串。 如果 path 作为 <Buffer> 传递,则 readStream.path 将是一个 <Buffer>。 如果指定了 fd,则 readStream.path 将是 undefined

readStream.pending#

如果底层文件尚未打开,即在触发 'ready' 事件之前,则此属性为 true

类: fs.Stats#

<fs.Stats> 对象提供有关文件的信息。

fs.stat()fs.lstat()fs.fstat() 及其同步对应方法返回的对象是此类型。 如果传递给这些方法的 options 中的 bigint 为 true,则数值将是 bigint 而不是 number,并且该对象将包含带有 Ns 后缀的附加纳秒精度属性。 不得使用 new 关键字直接创建 Stat 对象。

Stats {
  dev: 2114,
  ino: 48064969,
  mode: 33188,
  nlink: 1,
  uid: 85,
  gid: 100,
  rdev: 0,
  size: 527,
  blksize: 4096,
  blocks: 8,
  atimeMs: 1318289051000.1,
  mtimeMs: 1318289051000.1,
  ctimeMs: 1318289051000.1,
  birthtimeMs: 1318289051000.1,
  atime: Mon, 10 Oct 2011 23:24:11 GMT,
  mtime: Mon, 10 Oct 2011 23:24:11 GMT,
  ctime: Mon, 10 Oct 2011 23:24:11 GMT,
  birthtime: Mon, 10 Oct 2011 23:24:11 GMT } 

bigint 版本

BigIntStats {
  dev: 2114n,
  ino: 48064969n,
  mode: 33188n,
  nlink: 1n,
  uid: 85n,
  gid: 100n,
  rdev: 0n,
  size: 527n,
  blksize: 4096n,
  blocks: 8n,
  atimeMs: 1318289051000n,
  mtimeMs: 1318289051000n,
  ctimeMs: 1318289051000n,
  birthtimeMs: 1318289051000n,
  atimeNs: 1318289051000000000n,
  mtimeNs: 1318289051000000000n,
  ctimeNs: 1318289051000000000n,
  birthtimeNs: 1318289051000000000n,
  atime: Mon, 10 Oct 2011 23:24:11 GMT,
  mtime: Mon, 10 Oct 2011 23:24:11 GMT,
  ctime: Mon, 10 Oct 2011 23:24:11 GMT,
  birthtime: Mon, 10 Oct 2011 23:24:11 GMT } 
stats.isBlockDevice()#

如果 <fs.Stats> 对象描述的是块设备,则返回 true

stats.isCharacterDevice()#

如果 <fs.Stats> 对象描述的是字符设备,则返回 true

stats.isDirectory()#

如果 <fs.Stats> 对象描述的是文件系统目录,则返回 true

如果 <fs.Stats> 对象是通过在解析为目录的符号链接上调用 fs.lstat() 获得的,则此方法将返回 false。 这是因为 fs.lstat() 返回有关符号链接本身的信息,而不是它解析到的路径的信息。

stats.isFIFO()#

如果 <fs.Stats> 对象描述的是先进先出 (FIFO) 管道,则返回 true

stats.isFile()#

如果 <fs.Stats> 对象描述的是常规文件,则返回 true

stats.isSocket()#

如果 <fs.Stats> 对象描述的是套接字,则返回 true

stats.isSymbolicLink()#

如果 <fs.Stats> 对象描述的是符号链接,则返回 true

此方法仅在使用 fs.lstat() 时有效。

stats.dev#

包含该文件的设备的数字标识符。

stats.ino#

该文件的文件系统特定的 "Inode" 编号。

stats.mode#

描述文件类型和模式的位字段。

stats.nlink#

该文件存在的硬链接数。

stats.uid#

拥有该文件的用户的数字用户标识符 (POSIX)。

stats.gid#

拥有该文件的组的数字组标识符 (POSIX)。

stats.rdev#

如果文件表示设备,则为数字设备标识符。

stats.size#

文件的大小(以字节为单位)。

如果底层文件系统不支持获取文件的大小,则该值将为 0

stats.blksize#

i/o 操作的文件系统块大小。

stats.blocks#

为此文件分配的块数。

stats.atimeMs#

时间戳,指示自 POSIX 纪元以来以毫秒为单位表示的上次访问此文件的时间。

stats.mtimeMs#

时间戳,指示自 POSIX 纪元以来以毫秒为单位表示的上次修改此文件的时间。

stats.ctimeMs#

时间戳,指示自 POSIX 纪元以来以毫秒为单位表示的上次更改文件状态的时间。

stats.birthtimeMs#

时间戳,指示自 POSIX 纪元以来以毫秒为单位表示的此文件的创建时间。

stats.atimeNs#

仅当将 bigint: true 传递到生成对象的方法中时才存在。 时间戳,指示自 POSIX 纪元以来以纳秒为单位表示的上次访问此文件的时间。

stats.mtimeNs#

仅当将 bigint: true 传递到生成对象的方法中时才存在。 时间戳,指示自 POSIX 纪元以来以纳秒为单位表示的上次修改此文件的时间。

stats.ctimeNs#

仅当将 bigint: true 传递到生成对象的方法中时才存在。 时间戳,指示自 POSIX 纪元以来以纳秒为单位表示的上次更改文件状态的时间。

stats.birthtimeNs#

仅当将 bigint: true 传递到生成对象的方法中时才存在。 时间戳,指示自 POSIX 纪元以来以纳秒为单位表示的此文件的创建时间。

stats.atime#

时间戳,指示上次访问此文件的时间。

stats.mtime#

时间戳,指示上次修改此文件的时间。

stats.ctime#

时间戳,指示上次更改文件状态的时间。

stats.birthtime#

时间戳,指示此文件的创建时间。

Stat 时间值#

atimeMsmtimeMsctimeMsbirthtimeMs 属性是数值,包含以毫秒为单位的相应时间。 它们的精度是特定于平台的。 当将 bigint: true 传递到生成对象的方法中时,这些属性将是 bigint,否则它们将是 numbers

atimeNsmtimeNsctimeNsbirthtimeNs 属性是 bigint,包含以纳秒为单位的相应时间。 它们仅在将 bigint: true 传递到生成对象的方法中时才存在。 它们的精度是特定于平台的。

atimemtimectimebirthtime 是各种时间的 Date 对象替代表示形式。 Date 和数字值未连接。 分配新的数字值或改变 Date 值将不会反映在相应的替代表示形式中。

stat 对象中的时间具有以下语义

  • atime "访问时间": 上次访问文件数据的时间。 由 mknod(2)utimes(2)read(2) 系统调用更改。
  • mtime "修改时间": 上次修改文件数据的时间。 由 mknod(2)utimes(2)write(2) 系统调用更改。
  • ctime "更改时间": 上次更改文件状态(inode 数据修改)的时间。 由 chmod(2)chown(2)link(2)mknod(2)rename(2)unlink(2)utimes(2)read(2)write(2) 系统调用更改。
  • birthtime “创建时间”:文件创建的时间。在文件创建时设置一次。在没有创建时间的文件系统中,此字段可能保存 ctime1970-01-01T00:00Z(即 Unix 时间戳 0)。在这种情况下,该值可能大于 atimemtime。在 Darwin 和其他 FreeBSD 变体中,如果使用 utimes(2) 系统调用将 atime 显式设置为早于当前 birthtime 的值,也会设置此值。

在 Node.js 0.12 之前,ctime 在 Windows 系统上保存了 birthtime。 从 0.12 开始,ctime 不是“创建时间”,并且在 Unix 系统上,它从来都不是。

类: fs.StatFs#

提供有关已挂载文件系统的信息。

fs.statfs() 及其同步对应项返回的对象属于此类型。 如果传递给这些方法的 options 中的 biginttrue,则数值将为 bigint 而不是 number

StatFs {
  type: 1397114950,
  bsize: 4096,
  blocks: 121938943,
  bfree: 61058895,
  bavail: 61058895,
  files: 999,
  ffree: 1000000
} 

bigint 版本

StatFs {
  type: 1397114950n,
  bsize: 4096n,
  blocks: 121938943n,
  bfree: 61058895n,
  bavail: 61058895n,
  files: 999n,
  ffree: 1000000n
} 
statfs.bavail#

非特权用户可用的空闲块。

statfs.bfree#

文件系统中的空闲块。

statfs.blocks#

文件系统中的数据块总数。

statfs.bsize#

最佳传输块大小。

statfs.ffree#

文件系统中的空闲文件节点。

statfs.files#

文件系统中的文件节点总数。

statfs.type#

文件系统的类型。

类: fs.WriteStream#

<fs.WriteStream> 的实例使用 fs.createWriteStream() 函数创建并返回。

事件: 'close'#

<fs.WriteStream> 的底层文件描述符已关闭时触发。

事件: 'open'#

<fs.WriteStream> 的文件打开时触发。

事件: 'ready'#

<fs.WriteStream> 准备好使用时触发。

'open' 之后立即触发。

writeStream.bytesWritten#

到目前为止写入的字节数。不包括仍在排队等待写入的数据。

writeStream.close([callback])#

关闭 writeStream。 可选地接受一个回调,该回调将在 writeStream 关闭后执行。

writeStream.path#

流正在写入的文件的路径,如 fs.createWriteStream() 的第一个参数中所指定。 如果 path 作为字符串传递,则 writeStream.path 将是一个字符串。 如果 path 作为 <Buffer> 传递,则 writeStream.path 将是一个 <Buffer>

writeStream.pending#

如果底层文件尚未打开,即在触发 'ready' 事件之前,则此属性为 true

fs.constants#

返回一个对象,其中包含文件系统操作常用的常量。

FS 常量#

以下常量由 fs.constantsfsPromises.constants 导出。

并非每个常量都可在每个操作系统上使用; 这对于 Windows 尤其重要,因为许多 POSIX 特定的定义不可用。 对于可移植的应用程序,建议在使用前检查它们是否存在。

要使用多个常量,请使用按位 OR | 运算符。

示例

import { open, constants } from 'node:fs';

const {
  O_RDWR,
  O_CREAT,
  O_EXCL,
} = constants;

open('/path/to/my/file', O_RDWR | O_CREAT | O_EXCL, (err, fd) => {
  // ...
}); 
文件访问常量#

以下常量用作传递给 fsPromises.access()fs.access()fs.accessSync()mode 参数。

常量 描述
F_OK 标志,指示文件对于调用进程是可见的。 这对于确定文件是否存在很有用,但与 rwx 权限无关。 如果未指定任何模式,则为默认值。
R_OK 标志,指示该文件可以被调用进程读取。
W_OK 标志,指示该文件可以被调用进程写入。
X_OK 标志,指示该文件可以被调用进程执行。 这对 Windows 没有影响(行为类似于 fs.constants.F_OK)。

这些定义在 Windows 上也可用。

文件复制常量#

以下常量用于 fs.copyFile()

常量 描述
COPYFILE_EXCL 如果存在,并且目标路径已存在,则复制操作将失败并显示错误。
COPYFILE_FICLONE 如果存在,则复制操作将尝试创建写时复制的 reflink。 如果底层平台不支持写时复制,则使用回退复制机制。
COPYFILE_FICLONE_FORCE 如果存在,则复制操作将尝试创建写时复制的 reflink。 如果底层平台不支持写时复制,则操作将失败并显示错误。

这些定义在 Windows 上也可用。

文件打开常量#

以下常量用于 fs.open()

常量 描述
O_RDONLY 标志,指示以只读方式打开文件。
O_WRONLY 标志,指示以只写方式打开文件。
O_RDWR 标志,指示以读写方式打开文件。
O_CREAT 标志,指示如果文件不存在则创建文件。
O_EXCL 标志,指示如果设置了 O_CREAT 标志并且文件已存在,则打开文件应失败。
O_NOCTTY 标志,指示如果路径标识终端设备,则打开该路径不应导致该终端成为该进程的控制终端(如果该进程尚未拥有一个)。
O_TRUNC 标志,指示如果文件存在并且是常规文件,并且文件已成功打开以进行写访问,则其长度应截断为零。
O_APPEND 标志,指示数据将附加到文件末尾。
O_DIRECTORY 标志,指示如果路径不是目录,则打开应失败。
O_NOATIME 标志,指示对文件系统的读取访问不再导致更新与文件关联的 atime 信息。 此标志仅在 Linux 操作系统上可用。
O_NOFOLLOW 标志,指示如果路径是符号链接,则打开应失败。
O_SYNC 标志,指示该文件以同步 I/O 方式打开,并且写操作等待文件完整性。
O_DSYNC 标志,指示该文件以同步 I/O 方式打开,并且写操作等待数据完整性。
O_SYMLINK 标志,指示打开符号链接本身,而不是它指向的资源。
O_DIRECT 设置后,将尝试最小化文件 I/O 的缓存效果。
O_NONBLOCK 标志,指示尽可能以非阻塞模式打开文件。
UV_FS_O_FILEMAP 设置后,使用内存文件映射来访问该文件。 此标志仅在 Windows 操作系统上可用。 在其他操作系统上,此标志将被忽略。

在 Windows 上,只有 O_APPENDO_CREATO_EXCLO_RDONLYO_RDWRO_TRUNCO_WRONLYUV_FS_O_FILEMAP 可用。

文件类型常量#

以下常量用于 <fs.Stats> 对象的 mode 属性,以确定文件的类型。

常量 描述
S_IFMT 用于提取文件类型代码的位掩码。
S_IFREG 常规文件的文件类型常量。
S_IFDIR 目录的文件类型常量。
S_IFCHR 面向字符的设备文件的文件类型常量。
S_IFBLK 面向块的设备文件的文件类型常量。
S_IFIFO FIFO/管道的文件类型常量。
S_IFLNK 符号链接的文件类型常量。
S_IFSOCK 套接字的文件类型常量。

在 Windows 上,只有 S_IFCHRS_IFDIRS_IFLNKS_IFMTS_IFREG 可用。

文件模式常量#

以下常量用于 <fs.Stats> 对象的 mode 属性,用于确定文件的访问权限。

常量 描述
S_IRWXU 文件模式,指示所有者可读、可写和可执行。
S_IRUSR 文件模式,指示所有者可读。
S_IWUSR 文件模式,指示所有者可写。
S_IXUSR 文件模式,指示所有者可执行。
S_IRWXG 文件模式,指示群组可读、可写和可执行。
S_IRGRP 文件模式,指示群组可读。
S_IWGRP 文件模式,指示群组可写。
S_IXGRP 文件模式,指示群组可执行。
S_IRWXO 文件模式,指示其他人可读、可写和可执行。
S_IROTH 文件模式,指示其他人可读。
S_IWOTH 文件模式,指示其他人可写。
S_IXOTH 文件模式,指示其他人可执行。

在 Windows 上,仅 S_IRUSRS_IWUSR 可用。

注意#

基于回调和基于 Promise 的操作的排序#

由于它们是由底层线程池异步执行的,因此在使用基于回调或基于 Promise 的方法时,不能保证排序。

例如,以下代码容易出错,因为 fs.stat() 操作可能会在 fs.rename() 操作之前完成

const fs = require('node:fs');

fs.rename('/tmp/hello', '/tmp/world', (err) => {
  if (err) throw err;
  console.log('renamed complete');
});
fs.stat('/tmp/world', (err, stats) => {
  if (err) throw err;
  console.log(`stats: ${JSON.stringify(stats)}`);
}); 

重要的是通过等待一个操作的结果,然后再调用另一个操作来正确地对操作进行排序

import { rename, stat } from 'node:fs/promises';

const oldPath = '/tmp/hello';
const newPath = '/tmp/world';

try {
  await rename(oldPath, newPath);
  const stats = await stat(newPath);
  console.log(`stats: ${JSON.stringify(stats)}`);
} catch (error) {
  console.error('there was an error:', error.message);
}const { rename, stat } = require('node:fs/promises');

(async function(oldPath, newPath) {
  try {
    await rename(oldPath, newPath);
    const stats = await stat(newPath);
    console.log(`stats: ${JSON.stringify(stats)}`);
  } catch (error) {
    console.error('there was an error:', error.message);
  }
})('/tmp/hello', '/tmp/world');

或者,当使用回调 API 时,将 fs.stat() 调用移动到 fs.rename() 操作的回调中

import { rename, stat } from 'node:fs';

rename('/tmp/hello', '/tmp/world', (err) => {
  if (err) throw err;
  stat('/tmp/world', (err, stats) => {
    if (err) throw err;
    console.log(`stats: ${JSON.stringify(stats)}`);
  });
});const { rename, stat } = require('node:fs/promises');

rename('/tmp/hello', '/tmp/world', (err) => {
  if (err) throw err;
  stat('/tmp/world', (err, stats) => {
    if (err) throw err;
    console.log(`stats: ${JSON.stringify(stats)}`);
  });
});

文件路径#

大多数 fs 操作接受文件路径,这些路径可以字符串、<Buffer> 或使用 file: 协议的 <URL> 对象的形式指定。

字符串路径#

字符串路径被解释为 UTF-8 字符序列,用于标识绝对或相对文件名。 相对路径将根据调用 process.cwd() 确定的当前工作目录进行解析。

在 POSIX 上使用绝对路径的示例

import { open } from 'node:fs/promises';

let fd;
try {
  fd = await open('/open/some/file.txt', 'r');
  // Do something with the file
} finally {
  await fd?.close();
} 

在 POSIX 上使用相对路径的示例(相对于 process.cwd()

import { open } from 'node:fs/promises';

let fd;
try {
  fd = await open('file.txt', 'r');
  // Do something with the file
} finally {
  await fd?.close();
} 
文件 URL 路径#

对于大多数 node:fs 模块函数,pathfilename 参数可以作为使用 file: 协议的 <URL> 对象传递。

import { readFileSync } from 'node:fs';

readFileSync(new URL('file:///tmp/hello')); 

file: URL 始终是绝对路径。

特定于平台的注意事项#

在 Windows 上,具有主机名的 file: <URL> 会转换为 UNC 路径,而具有驱动器盘符的 file: <URL> 会转换为本地绝对路径。 没有主机名和驱动器盘符的 file: <URL> 将导致错误

import { readFileSync } from 'node:fs';
// On Windows :

// - WHATWG file URLs with hostname convert to UNC path
// file://hostname/p/a/t/h/file => \\hostname\p\a\t\h\file
readFileSync(new URL('file://hostname/p/a/t/h/file'));

// - WHATWG file URLs with drive letters convert to absolute path
// file:///C:/tmp/hello => C:\tmp\hello
readFileSync(new URL('file:///C:/tmp/hello'));

// - WHATWG file URLs without hostname must have a drive letters
readFileSync(new URL('file:///notdriveletter/p/a/t/h/file'));
readFileSync(new URL('file:///c/p/a/t/h/file'));
// TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must be absolute 

带有驱动器盘符的 file: <URL> 必须使用 : 作为驱动器盘符后的分隔符。 使用另一个分隔符将导致错误。

在所有其他平台上,具有主机名的 file: <URL> 不受支持,并将导致错误

import { readFileSync } from 'node:fs';
// On other platforms:

// - WHATWG file URLs with hostname are unsupported
// file://hostname/p/a/t/h/file => throw!
readFileSync(new URL('file://hostname/p/a/t/h/file'));
// TypeError [ERR_INVALID_FILE_URL_PATH]: must be absolute

// - WHATWG file URLs convert to absolute path
// file:///tmp/hello => /tmp/hello
readFileSync(new URL('file:///tmp/hello')); 

具有编码斜杠字符的 file: <URL> 将在所有平台上导致错误

import { readFileSync } from 'node:fs';

// On Windows
readFileSync(new URL('file:///C:/p/a/t/h/%2F'));
readFileSync(new URL('file:///C:/p/a/t/h/%2f'));
/* TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must not include encoded
\ or / characters */

// On POSIX
readFileSync(new URL('file:///p/a/t/h/%2F'));
readFileSync(new URL('file:///p/a/t/h/%2f'));
/* TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must not include encoded
/ characters */ 

在 Windows 上,具有编码反斜杠的 file: <URL> 将导致错误

import { readFileSync } from 'node:fs';

// On Windows
readFileSync(new URL('file:///C:/path/%5C'));
readFileSync(new URL('file:///C:/path/%5c'));
/* TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must not include encoded
\ or / characters */ 
Buffer 路径#

使用 <Buffer> 指定的路径主要在某些将文件路径视为不透明字节序列的 POSIX 操作系统上很有用。 在此类系统上,单个文件路径可能包含使用多种字符编码的子序列。 与字符串路径一样,<Buffer> 路径可以是相对的或绝对的

在 POSIX 上使用绝对路径的示例

import { open } from 'node:fs/promises';
import { Buffer } from 'node:buffer';

let fd;
try {
  fd = await open(Buffer.from('/open/some/file.txt'), 'r');
  // Do something with the file
} finally {
  await fd?.close();
} 
Windows 上的每个驱动器工作目录#

在 Windows 上,Node.js 遵循每个驱动器工作目录的概念。 当使用不带反斜杠的驱动器路径时,可以观察到此行为。 例如,fs.readdirSync('C:\\') 可能会返回与 fs.readdirSync('C:') 不同的结果。 有关更多信息,请参见此 MSDN 页面

文件描述符#

在 POSIX 系统上,对于每个进程,内核都会维护一个当前打开的文件和资源的表。 每个打开的文件都分配一个简单的数字标识符,称为文件描述符。 在系统级别,所有文件系统操作都使用这些文件描述符来标识和跟踪每个特定文件。 Windows 系统使用一种不同的但概念上相似的机制来跟踪资源。 为了简化用户操作,Node.js 抽象了操作系统之间的差异,并为所有打开的文件分配一个数字文件描述符。

基于回调的 fs.open() 和同步的 fs.openSync() 方法打开一个文件并分配一个新的文件描述符。 分配后,文件描述符可用于从文件中读取数据、向文件中写入数据或请求有关该文件的信息。

操作系统限制了在任何给定时间可以打开的文件描述符的数量,因此在操作完成后关闭描述符至关重要。 否则将导致内存泄漏,最终导致应用程序崩溃。

import { open, close, fstat } from 'node:fs';

function closeFd(fd) {
  close(fd, (err) => {
    if (err) throw err;
  });
}

open('/open/some/file.txt', 'r', (err, fd) => {
  if (err) throw err;
  try {
    fstat(fd, (err, stat) => {
      if (err) {
        closeFd(fd);
        throw err;
      }

      // use stat

      closeFd(fd);
    });
  } catch (err) {
    closeFd(fd);
    throw err;
  }
}); 

基于 Promise 的 API 使用 <FileHandle> 对象代替数字文件描述符。 这些对象由系统更好地管理,以确保资源不会泄漏。 但是,仍然需要在操作完成后关闭它们

import { open } from 'node:fs/promises';

let file;
try {
  file = await open('/open/some/file.txt', 'r');
  const stat = await file.stat();
  // use stat
} finally {
  await file.close();
} 

线程池的使用#

所有基于回调和基于 Promise 的文件系统 API(除了 fs.FSWatcher())都使用 libuv 的线程池。 这可能会对某些应用程序产生令人惊讶和负面的性能影响。 有关更多信息,请参见 UV_THREADPOOL_SIZE 文档。

文件系统标志#

以下标志在 flag 选项接受字符串的任何地方可用。

  • 'a':打开文件以进行追加。 如果该文件不存在,则创建该文件。

  • 'ax':类似于 'a',但如果路径存在则失败。

  • 'a+':打开文件以进行读取和追加。 如果该文件不存在,则创建该文件。

  • 'ax+':类似于 'a+',但如果路径存在则失败。

  • 'as':以同步模式打开文件以进行追加。 如果该文件不存在,则创建该文件。

  • 'as+':以同步模式打开文件以进行读取和追加。 如果该文件不存在,则创建该文件。

  • 'r':打开文件以进行读取。 如果该文件不存在,则会发生异常。

  • 'rs':以同步模式打开文件以进行读取。 如果该文件不存在,则会发生异常。

  • 'r+':打开文件以进行读取和写入。 如果该文件不存在,则会发生异常。

  • 'rs+':以同步模式打开文件以进行读取和写入。 指示操作系统绕过本地文件系统缓存。

    这主要用于在 NFS 挂载上打开文件,因为它允许跳过可能过时的本地缓存。 它对 I/O 性能有非常实际的影响,因此除非需要,否则不建议使用此标志。

    这不会将 fs.open()fsPromises.open() 转换为同步阻塞调用。 如果需要同步操作,则应使用类似 fs.openSync() 的操作。

  • 'w':打开文件以进行写入。 如果该文件不存在,则创建该文件(如果存在),则将其截断。

  • 'wx':类似于 'w',但如果路径存在则失败。

  • 'w+':打开文件以进行读取和写入。 如果该文件不存在,则创建该文件(如果存在),则将其截断。

  • 'wx+':类似于 'w+',但如果路径存在则失败。

flag 也可以是数字,如 open(2) 中所述; 常用的常量可从 fs.constants 获得。 在 Windows 上,标志会转换为其等效标志(如果适用),例如,O_WRONLY 转换为 FILE_GENERIC_WRITE,或者 O_EXCL|O_CREAT 转换为 CREATE_NEW,如 CreateFileW 所接受。

独占标志 'x'open(2) 中的 O_EXCL 标志)会导致操作在路径已存在时返回错误。 在 POSIX 上,如果路径是符号链接,则即使链接指向不存在的路径,使用 O_EXCL 也会返回错误。 独占标志可能不适用于网络文件系统。

在 Linux 上,当文件以追加模式打开时,位置写入不起作用。 内核忽略位置参数,总是将数据追加到文件末尾。

修改文件而不是替换文件可能需要将 flag 选项设置为 'r+' 而不是默认的 'w'

某些标志的行为特定于平台。 因此,在 macOS 和 Linux 上使用 'a+' 标志打开目录(如下面的示例所示)将返回错误。 相比之下,在 Windows 和 FreeBSD 上,将返回文件描述符或 FileHandle

// macOS and Linux
fs.open('<directory>', 'a+', (err, fd) => {
  // => [Error: EISDIR: illegal operation on a directory, open <directory>]
});

// Windows and FreeBSD
fs.open('<directory>', 'a+', (err, fd) => {
  // => null, <fd>
}); 

在 Windows 上,使用 'w' 标志(通过 fs.open()fs.writeFile()fsPromises.open())打开现有的隐藏文件将失败并显示 EPERM。 可以使用 'r+' 标志打开现有的隐藏文件以进行写入。

可以调用 fs.ftruncate()filehandle.truncate() 来重置文件内容。