> ## Documentation Index
> Fetch the complete documentation index at: https://bun.zhcndoc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 文件 I/O

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

<Note>
  此页面记录的 `Bun.file` 和 `Bun.write` API 经过高度优化，代表了使用 Bun 执行文件系统任务的推荐方式。对于尚未通过 `Bun.file` 提供的操作，如 `mkdir` 或 `readdir`，可以使用 Bun 对 [`node:fs`](https://nodejs.org/api/fs.html) 模块的[几乎完整](/runtime/nodejs-compat#node-fs) 实现。
</Note>

***

## 读取文件 (`Bun.file()`)

`Bun.file(path): BunFile`

使用函数 `Bun.file(path)` 创建一个 `BunFile` 实例。`BunFile` 代表一个惰性加载的文件；初始化时并不会真正从磁盘读取文件。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const foo = Bun.file("foo.txt"); // 相对于当前工作目录
foo.size; // 字节数
foo.type; // MIME 类型
```

该引用遵循 [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) 接口，因此内容可以以多种格式读取。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const foo = Bun.file("foo.txt");

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

文件引用也可以通过数字 [文件描述符](https://en.wikipedia.org/wiki/File_descriptor) 或 `file://` URL 创建。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
Bun.file(1234);
Bun.file(new URL(import.meta.url)); // 当前文件的引用
```

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

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
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` 传递第二个参数覆盖它。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const notreal = Bun.file("notreal.json", { type: "application/json" });
notreal.type; // => "application/json;charset=utf-8"
```

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

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
Bun.stdin; // 只读
Bun.stdout;
Bun.stderr;
```

### 删除文件 (`file.delete()`)

可以通过调用 `.delete()` 函数删除文件。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
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`

所有可能的组合都使用当前平台上最快的系统调用处理。

<Accordion title="查看系统调用">
  | 输出      | 输入        | 系统调用                       | 平台    |
  | ------- | --------- | -------------------------- | ----- |
  | 文件      | 文件        | copy\_file\_range          | Linux |
  | 文件      | 管道        | sendfile                   | Linux |
  | 管道      | 管道        | splice                     | Linux |
  | 终端      | 文件        | sendfile                   | Linux |
  | 终端      | 终端        | sendfile                   | Linux |
  | 套接字     | 文件或管道     | sendfile（如果是 http，非 https） | Linux |
  | 文件（不存在） | 文件（路径）    | clonefile                  | macOS |
  | 文件（已存在） | 文件        | fcopyfile                  | macOS |
  | 文件      | Blob 或字符串 | write                      | macOS |
  | 文件      | Blob 或字符串 | write                      | Linux |
</Accordion>

将字符串写入磁盘：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const data = `那是最好的时代，那是最坏的时代。`;
await Bun.write("output.txt", data);
```

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

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const input = Bun.file("input.txt");
const output = Bun.file("output.txt"); // 还不存在!
await Bun.write(output, input);
```

将字节数组写入磁盘：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const encoder = new TextEncoder();
const data = encoder.encode("datadatadata"); // Uint8Array
await Bun.write("output.txt", data);
```

将文件内容写入 `stdout`：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const input = Bun.file("input.txt");
await Bun.write(Bun.stdout, input);
```

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

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const response = await fetch("https://bun.com");
await Bun.write("index.html", response);
```

***

## 使用 `FileSink` 进行增量写入

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

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const file = Bun.file("output.txt");
const writer = file.writer();
```

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

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const file = Bun.file("output.txt");
const writer = file.writer();

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

这些数据块将在内部缓存。要将缓存写入磁盘，使用 `.flush()`。此方法返回已写入字节数。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
writer.flush(); // 将缓存写入磁盘
```

当 `FileSink` 的 *高水位线*（内部缓存满）达到时，缓存也会自动刷新。可通过配置此参数来调整。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const file = Bun.file("output.txt");
const writer = file.writer({ highWaterMark: 1024 * 1024 }); // 1MB
```

刷新缓存并关闭文件：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
writer.end();
```

注意，默认情况下，`bun` 进程会保持运行，直到显式调用 `.end()` 关闭此 `FileSink`。如果要取消此行为，可以调用该实例的 `unref`。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
writer.unref();

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

***

## 目录操作

Bun's implementation of `node:fs` is fast. Use `node:fs` for working with directories in Bun.

### 读取目录（readdir）

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

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
import { readdir } from "node:fs/promises";

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

#### 递归读取目录

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

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
import { readdir } from "node:fs/promises";

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

### 创建目录（mkdir）

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

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
import { mkdir } from "node:fs/promises";

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

***

## 性能基准

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

```ts cat.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 用法
// 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));
```

运行该文件：

```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun ./cat.ts ./path-to-file
```

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

<Frame>
  <img src="https://mintcdn.com/bun-zhcndoc/7hwCkUCcx3ux5DPj/images/cat.jpg?fit=max&auto=format&n=7hwCkUCcx3ux5DPj&q=85&s=d649e7bd65e79eeaff0908fcca76de7b" alt="Cat 截图" width="1194" height="1143" data-path="images/cat.jpg" />
</Frame>

***

## 参考

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
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>;
  writer(params: { highWaterMark?: number }): FileSink;
  exists(): Promise<boolean>;
}

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