文件系统#

稳定性: 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
}

Promises API#

fs/promises API 提供异步文件系统方法,这些方法返回 Promise。

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

类:FileHandle#

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

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

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

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

<stream.Readable> 的 16 KiB 默认 highWaterMark 不同,此方法返回的流的默认 highWaterMark 为 64 KiB。

options 可以包含 startend 值,用于读取文件中的字节范围,而不是整个文件。startend 都是包含的,从 0 开始计数,允许的值在 [0, Number.MAX_SAFE_INTEGER] 范围内。如果省略 startstartundefinedfilehandle.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 设置为 true(默认行为),则在 'error''finish' 时,文件描述符将自动关闭。如果 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([options])#

稳定性: 1 - 实验性

返回一个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 <整数>buffer 中开始写入数据的起始位置。
  • length <整数>buffer 中写入的字节数。默认值:buffer.byteLength - offset
  • position <整数> | <null> 从文件开头开始写入 buffer 中数据的偏移量。如果 position 不是一个 数字,数据将写入当前位置。有关详细信息,请参阅 POSIX pwrite(2) 文档。默认值:null
  • 返回值:<Promise>

buffer 写入文件。

该 Promise 将以包含两个属性的对象来完成

在同一个文件上多次使用 filehandle.write() 而不等待 Promise 完成(或拒绝)是不安全的。对于这种情况,请使用 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 <整数> 写入的字节数
  • buffer <string> 对写入的 string 的引用。

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

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

filehandle.writeFile(data, options)#

异步将数据写入文件,如果文件已存在则替换文件。data 可以是字符串、缓冲区、<AsyncIterable><Iterable> 对象。promise 成功后将以无参数的形式完成。

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

<FileHandle> 必须支持写入。

在同一个文件上多次使用 filehandle.writeFile() 而不等待 promise 完成(或拒绝)是不安全的。

如果在一个文件句柄上调用了一个或多个 filehandle.write() 方法,然后又调用了 filehandle.writeFile() 方法,那么数据将从当前位置写入到文件末尾。它并不总是从文件开头开始写入。

filehandle.writev(buffers[, position])#

将一个 <ArrayBufferView> 数组写入文件。

Promise 将被一个包含两个属性的对象所实现

在同一个文件上多次调用 writev() 而不等待 Promise 实现(或拒绝)是不安全的。

在 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 中的任何一个按位或运算组成的掩码(例如 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> 指定复制操作行为的可选修饰符。可以创建由两个或多个值的按位 OR 组成的掩码(例如 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])#

稳定性: 1 - 实验性

  • src <string> | <URL> 要复制的源路径。
  • dest <string> | <URL> 要复制到的目标路径。
  • options <Object>
    • dereference <boolean> 解引用符号链接。默认: false
    • errorOnExist <boolean>forcefalse 且目标存在时,抛出错误。默认: false
    • filter <Function> 用于过滤复制的文件/目录的函数。返回 true 以复制项目,返回 false 以忽略它。忽略目录时,其所有内容也将被跳过。也可以返回一个解析为 truefalsePromise默认: undefined
    • 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.lchmod(path, mode)#

更改符号链接的权限。

此方法仅在 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 指向符号链接,在这种情况下,链接本身会被统计,而不是它指向的文件。有关更多详细信息,请参阅 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 <字符串> | <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 <字符串> | <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 命令的行为,请使用 fsPromises.rm() 以及选项 { recursive: true, force: true }

fsPromises.rm(path[, options])#

  • path <字符串> | <Buffer> | <URL>
  • options <Object>
    • force <boolean> 如果 path 不存在,当 true 时,将忽略异常。默认值: 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 参数不是字符串,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 可以是字符串、缓冲区、<AsyncIterable><Iterable> 对象。

如果 data 是缓冲区,则忽略 encoding 选项。

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

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

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

在同一个文件上多次使用 fsPromises.writeFile() 而不等待 promise 解决是不安全的。

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 中的任何一个按位或运算组成的掩码(例如 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 参数是一个数值位掩码,使用以下常量的逻辑或运算符创建。

常量八进制描述
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)#

稳定性: 1 - 实验性

  • src <string> | <URL> 要复制的源路径。
  • dest <string> | <URL> 要复制到的目标路径。
  • options <Object>
    • dereference <boolean> 解引用符号链接。默认: false
    • errorOnExist <boolean>forcefalse 且目标存在时,抛出错误。默认: false
    • filter <Function> 用于过滤复制的文件/目录的函数。返回 true 以复制项目,返回 false 以忽略它。忽略目录时,其所有内容也将被跳过。也可以返回一个解析为 truefalsePromise默认: undefined
    • force <boolean> 覆盖现有文件或目录。如果将此设置为 false 并且目标存在,则复制操作将忽略错误。使用 errorOnExist 选项更改此行为。默认值: true
    • mode <整数> 用于复制操作的修饰符。默认值: 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])#

<stream.Readable> 的 16 KiB 默认 highWaterMark 不同,此方法返回的流的默认 highWaterMark 为 64 KiB。

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

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

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

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

通过提供 fs 选项,可以覆盖 openreadclose 的对应 fs 实现。提供 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 设置为 true(默认行为),则在 'error''finish' 时,文件描述符将自动关闭。如果 autoClose 为 false,则即使发生错误,文件描述符也不会关闭。应用程序有责任关闭它并确保没有文件描述符泄漏。

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

通过提供 fs 选项,可以覆盖 openwritewritevclose 的对应 fs 实现。覆盖 write() 而不覆盖 writev() 会降低性能,因为某些优化(_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() 代替。

通过检查文件系统来测试给定路径是否存在。然后使用 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() 的原因之一。

不建议使用 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.lchmod(path, mode, callback)#

更改符号链接的权限。除了可能的异常之外,不会向完成回调提供任何参数。

此方法仅在 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)#

existingPathnewPath 创建一个新的链接。有关更多详细信息,请参阅 POSIX link(2) 文档。除了可能的异常之外,不会向完成回调提供任何参数。

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

检索由路径引用的符号链接的 <fs.Stats>。回调获取两个参数 (err, stats),其中 stats 是一个 <fs.Stats> 对象。lstat()stat() 相同,区别在于如果 path 是一个符号链接,则会对链接本身进行 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])#

稳定性: 1 - 实验性

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

在创建 <Blob> 之后,不得修改文件。任何修改都将导致读取 <Blob> 数据失败,并出现 DOMException 错误。在创建 Blob 时以及在每次读取之前,对文件进行同步状态操作,以检测文件数据是否已在磁盘上修改。

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() 版本,它将返回一个包含 bytesReadbuffer 属性的 Object 的 Promise。

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提供了更多信息,以及对不同Node.js版本中fs.readFile()在多种文件大小下的性能的详细分析。

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 解析为套接字或管道,则该函数将返回该对象的系统相关名称。

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 <字符串> | <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 命令的行为,请使用 fs.rm() 以及选项 { recursive: true, force: true }

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

  • path <字符串> | <Buffer> | <URL>
  • options <Object>
    • force <boolean> 如果 path 不存在,当 true 时,将忽略异常。默认值: 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 参数不是字符串,Node.js 将自动检测 target 类型并使用 'file''dir'。如果 target 不存在,将使用 'file'。Windows 连接点要求目标路径为绝对路径。使用 'junction' 时,target 参数将自动规范化为绝对路径。NTFS 卷上的连接点只能指向目录。

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

import { symlink } from 'node:fs';

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

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

$ 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 在不同平台上并不完全一致,并且在某些情况下不可用。

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

可用性#

此功能依赖于底层操作系统提供一种方法来通知文件系统更改。

  • 在 Linux 系统上,这使用 inotify(7)
  • 在 BSD 系统上,这使用 kqueue(2)
  • 在 macOS 上,这使用 kqueue(2) 用于文件和 FSEvents 用于目录。
  • 在 SunOS 系统(包括 Solaris 和 SmartOS)上,这使用 事件端口
  • 在 Windows 系统上,此功能依赖于 ReadDirectoryChangesW
  • 在 AIX 系统上,此功能依赖于 AHAFS,它必须启用。
  • 在 IBM i 系统上,此功能不受支持。

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

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

索引节点#

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

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 获取两个参数:当前状态对象和先前状态对象

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}`);
}); 

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

要通知文件何时被修改,而不仅仅是访问,需要比较 curr.mtimeMsprev.mtimeMs

fs.watchFile 操作导致 ENOENT 错误时,它将调用监听器一次,所有字段都为零(或对于日期,为 Unix 纪元)。如果文件稍后创建,则将再次调用监听器,并使用最新的统计对象。这是自 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() 版本,它将返回一个对具有 bytesWrittenbuffer 属性的 Object 的承诺。

在同一个文件上多次使用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 是缓冲区,则忽略 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',则数据将写入当前位置。

回调将给出三个参数:errbytesWrittenbuffersbytesWrittenbuffers中写入的字节数。

如果此方法被util.promisify()化,它将返回一个Object的承诺,该对象具有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])#

稳定性: 1 - 实验性

  • src <string> | <URL> 要复制的源路径。
  • dest <string> | <URL> 要复制到的目标路径。
  • options <Object>
    • dereference <boolean> 解引用符号链接。默认: false
    • errorOnExist <boolean>forcefalse 且目标存在时,抛出错误。默认: false
    • filter <Function> 用于过滤复制的文件/目录的函数。返回 true 表示复制项目,返回 false 表示忽略项目。当忽略目录时,其所有内容也将被跳过。默认值: undefined
    • 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.lchmodSync(path, mode)#

更改符号链接的权限。返回 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)#

existingPath 创建一个指向 newPath 的新链接。有关更多详细信息,请参阅 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 <字符串> | <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 <字符串> | <Buffer> | <URL>
  • options <Object>
    • force <boolean> 如果 path 不存在,当 true 时,将忽略异常。默认值: 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])#

检索 path<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 在资源关闭后将被 fulfilled。

dir.close(callback)#

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

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

dir.closeSync()#

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

dir.path#

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

dir.read()#

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

返回一个 promise,该 promise 将以 <fs.Dirent> 形式 fulfilled,如果不再有目录条目可读,则以 null 形式 fulfilled。

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

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#

稳定性:1 – 实验性

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

dirent.path#

稳定性: 0 - 已弃用: 请使用 dirent.parentPath 代替。

dirent.parentPath 的别名。

类: fs.FSWatcher#

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

所有 <fs.FSWatcher> 对象在特定监视文件被修改时都会发出 'change' 事件。

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

当监视的目录或文件发生更改时发出。有关更多详细信息,请参阅 fs.watch()

filename 参数可能不会提供,具体取决于操作系统支持。如果提供了 filename,则如果 fs.watch() 被调用时其 encoding 选项设置为 'buffer',则它将作为 <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.createReadStream() 函数创建并返回 <fs.ReadStream> 的实例。

事件: '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#

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

类:fs.Stats#

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

fs.stat()fs.lstat()fs.fstat() 及其同步对应项返回的对象属于此类型。如果传递给这些方法的 options 中的 bigint 为 true,则数值将为 bigint 而不是 number,并且该对象将包含以 Ns 为后缀的附加纳秒精度属性。

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.isBlockDevice()#

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

stats.isCharacterDevice()#

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

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

stats.isDirectory()#

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

stats.isFIFO()#

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

stats.isFile()#

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

stats.isSocket()#

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

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

stats.isSymbolicLink()#

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

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,否则它们将是 数字

atimeNsmtimeNsctimeNsbirthtimeNs 属性是 大整数,以纳秒为单位保存相应的时间。它们仅在将 bigint: true 传递给生成对象的函数时才会出现。它们的精度是平台特定的。

atimemtimectimebirthtimeDate 对象对各种时间的替代表示。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#

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

fs.constants#

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

FS 常量#

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

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

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

示例

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> 指定的路径主要在某些将文件路径视为不透明字节序列的 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() 来重置文件内容。