运行时包管理器打包器测试运行器代码检查格式化指南参考博客反馈

文件 I/O

Bun 提供了一组经过优化的 API 用于读取和写入文件。


读取文件 (Bun.file())

Bun.file(path): BunFile

使用 Bun.file(path) 函数创建一个 BunFile 实例。BunFile 表示一个延迟加载的文件;初始化它不会从磁盘读取文件。

TypeScript
const foo = Bun.file("foo.txt"); // 相对于当前工作目录
foo.size; // 字节数
foo.type; // MIME 类型

该引用符合 Blob 接口,因此你可以使用多种格式读取内容。

TypeScript
const foo = Bun.file("foo.txt");

await foo.text(); // 内容作为字符串
await foo.json(); // 内容作为 JSON 对象
foo.stream(); // 内容作为 ReadableStream
await foo.arrayBuffer(); // 内容作为 ArrayBuffer
await foo.bytes(); // 内容作为 Uint8Array

你也可以从数字形式的文件描述符或 file:// URL 创建文件引用。

TypeScript
Bun.file(1234);
Bun.file(new URL(import.meta.url)); // 当前文件的引用

BunFile 可以指向磁盘上不存在文件的位置。

TypeScript
const notreal = Bun.file("notreal.txt");
notreal.size; // 0
notreal.type; // "text/plain;charset=utf-8"
const exists = await notreal.exists(); // false

默认 MIME 类型为 text/plain;charset=utf-8。通过向 Bun.file 传入第二个参数可以覆盖该类型。

TypeScript
const notreal = Bun.file("notreal.json", { type: "application/json" });
notreal.type; // => "application/json;charset=utf-8"

为了方便,Bun 将 stdin、stdout 和 stderr 作为 BunFile 实例暴露。

TypeScript
Bun.stdin; // 只读
Bun.stdout;
Bun.stderr;

删除文件 (file.delete())

调用 .delete() 删除文件。

TypeScript
await Bun.file("logs.json").delete();

写入文件 (Bun.write())

Bun.write(destination, data): Promise<number>

Bun.write 是一个多功能工具,用于将各种类型的负载写入磁盘。

第一个参数是 destination,可以是以下任意类型:

  • string:文件系统上的路径。使用 "path" 模块进行路径操作。
  • URL:file:// 描述符。
  • BunFile:文件引用。

第二个参数是要写入的数据,可以是以下任意类型:

  • string
  • Blob(包括 BunFile)
  • ArrayBuffer 或 SharedArrayBuffer
  • TypedArray(如 Uint8Array 等)
  • Response

Bun 会针对每种组合,使用当前平台上可用的最快系统调用。

查看系统调用
输出输入系统调用平台
文件文件copy_file_rangeLinux
文件管道sendfileLinux
管道管道spliceLinux
终端文件sendfileLinux
终端终端sendfileLinux
套接字文件或管道sendfile(如果是 http,则不是 https)Linux
文件(不存在)文件(路径)clonefilemacOS
文件(已存在)文件fcopyfilemacOS
文件Blob 或字符串writemacOS
文件Blob 或字符串writeLinux

将字符串写入磁盘:

TypeScript
const data = `那是最好的时代,那是最坏的时代。`;
await Bun.write("output.txt", data);

将文件复制到磁盘上另一个位置:

TypeScript
const input = Bun.file("input.txt");
const output = Bun.file("output.txt"); // 还不存在!
await Bun.write(output, input);

将字节数组写入磁盘:

TypeScript
const encoder = new TextEncoder();
const data = encoder.encode("datadatadata"); // Uint8Array
await Bun.write("output.txt", data);

将文件内容写入 stdout:

TypeScript
const input = Bun.file("input.txt");
await Bun.write(Bun.stdout, input);

将 HTTP 响应的正文写入磁盘:

TypeScript
const response = await fetch("https://bun.sh");
await Bun.write("index.html", response);

使用 FileSink 进行增量写入

Bun 提供了一个原生的增量写入文件 API,叫做 FileSink。从 BunFile 获取 FileSink 实例:

TypeScript
const file = Bun.file("output.txt");
const writer = file.writer();

增量写入文件时,调用 .write()。

TypeScript
const file = Bun.file("output.txt");
const writer = file.writer();

writer.write("那是最好的时代\n");
writer.write("那是最坏的时代\n");

FileSink 会在内部缓冲这些数据块。要将缓冲区刷新到磁盘,请使用 .flush()。它会返回已刷新的字节数。

TypeScript
writer.flush(); // 将缓存写入磁盘

当 FileSink 达到其_高水位标记_时,缓冲区也会自动刷新;也就是说,当其内部缓冲区已满时会自动刷新。你可以配置此值。

TypeScript
const file = Bun.file("output.txt");
const writer = file.writer({ highWaterMark: 1024 * 1024 }); // 1MB

刷新缓存并关闭文件:

TypeScript
writer.end();

默认情况下,bun 进程会保持运行,直到你使用 .end() 显式关闭此 FileSink。要选择不采用此行为,请对实例调用 "unref"。

TypeScript
writer.unref();

// 之后可再次调用以恢复保持进程存活
writer.ref();

目录操作

Bun 对 node:fs 的实现速度很快。在 Bun 中使用 node:fs 处理目录。

读取目录(readdir)

在 Bun 中,使用 node:fs 模块的 readdir 读取目录。

TypeScript
import { readdir } from "node:fs/promises";

// 读取当前目录下所有文件
const files = await readdir(import.meta.dir);

递归读取目录

使用 readdir 并开启 recursive: true 递归读取目录。

TypeScript
import { readdir } from "node:fs/promises";

// 递归读取上级目录中所有文件
const files = await readdir("../", { recursive: true });

创建目录(mkdir)

递归创建目录需使用 node:fs 的 mkdir:

TypeScript
import { mkdir } from "node:fs/promises";

await mkdir("path/to/dir", { recursive: true });

性能基准

以下是 Linux 下 cat 命令的三行实现。

cat.ts
// 用法
// bun ./cat.ts ./path-to-file

import { resolve } from "path";

const path = resolve(process.argv.at(-1));
await Bun.write(Bun.stdout, Bun.file(path));

运行该文件:

terminal
bun ./cat.ts ./path-to-file

在 Linux 上处理大文件时,速度是 GNU cat 的 2 倍。

猫截图

参考

TypeScript
interface Bun {
  stdin: BunFile;
  stdout: BunFile;
  stderr: BunFile;

  file(path: string | number | URL, options?: { type?: string }): BunFile;

  write(
    destination: string | number | BunFile | URL,
    input: string | Blob | ArrayBuffer | SharedArrayBuffer | TypedArray | Response,
  ): Promise<number>;
}

interface BunFile {
  readonly size: number;
  readonly type: string;

  text(): Promise<string>;
  stream(): ReadableStream;
  arrayBuffer(): Promise<ArrayBuffer>;
  json(): Promise<any>;
  bytes(): Promise<Uint8Array>;
  writer(params?: { highWaterMark?: number }): FileSink;
  exists(): Promise<boolean>;
  delete(): Promise<void>;
}

export interface FileSink {
  write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
  flush(): number | Promise<number>;
  end(error?: Error): number | Promise<number>;
  start(options?: { highWaterMark?: number }): void;
  ref(): void;
  unref(): void;
}