Skip to content

生产服务器通常从本地文件系统读取、上传和写入文件到 S3 兼容的对象存储服务,而不是本地文件系统。历史上,这意味着你在开发中使用的本地文件系统 API 不能在生产中使用。当你使用 Bun 时,情况就不同了。

Bun 的 S3 API 很快

Bun 提供快速、原生的绑定用于与 S3 兼容的对象存储服务交互。Bun 的 S3 API 设计简单,感觉类似于 fetch 的 ResponseBlob API(就像 Bun 的本地文件系统 API 一样)。

ts
import { s3, write, S3Client } from "bun";

// Bun.s3 读取环境变量的凭证
// file() 返回 S3 上文件的惰性引用
const metadata = s3.file("123.json");

// 从 S3 下载为 JSON
const data = await metadata.json();

// 上传到 S3
await write(metadata, JSON.stringify({ name: "John", age: 30 }));

// 预签名 URL(同步 - 不需要网络请求)
const url = metadata.presign({
  acl: "public-read",
  expiresIn: 60 * 60 * 24, // 1 天
});

// 删除文件
await metadata.delete();

S3 是 事实标准 的互联网文件系统。Bun 的 S3 API 适用于 S3 兼容存储服务,如:

  • AWS S3
  • Cloudflare R2
  • DigitalOcean Spaces
  • MinIO
  • Backblaze B2
  • ...以及任何其他 S3 兼容存储服务

基本用法

有几种方法可以与 Bun 的 S3 API 交互。

Bun.S3ClientBun.s3

Bun.s3 等同于 new Bun.S3Client(),依赖环境变量获取凭证。

要显式设置凭证,将它们传递给 Bun.S3Client 构造函数。

ts
import { S3Client } from "bun";

const client = new S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // sessionToken: "..."
  // acl: "public-read",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
  // endpoint: "https://<region>.digitaloceanspaces.com", // DigitalOcean Spaces
  // endpoint: "http://localhost:9000", // MinIO
});

// Bun.s3 是全局单例,等同于 `new Bun.S3Client()`

使用 S3 文件

S3Client 中的 file 方法返回 S3 上文件的惰性引用

ts
// S3 上文件的惰性引用
const s3file: S3File = client.file("123.json");

Bun.file(path) 一样,S3Clientfile 方法是同步的。在调用依赖网络请求的方法之前,它不会进行任何网络请求。

从 S3 读取文件

如果你使用过 fetch API,你熟悉 ResponseBlob API。S3File 扩展了 Blob。适用于 Blob 的方法也适用于 S3File

ts
// 将 S3File 读取为文本
const text = await s3file.text();

// 将 S3File 读取为 JSON
const json = await s3file.json();

// 将 S3File 读取为 ArrayBuffer
const buffer = await s3file.arrayBuffer();

// 仅获取前 1024 字节
const partial = await s3file.slice(0, 1024).text();

// 流式读取文件
const stream = s3file.stream();
for await (const chunk of stream) {
  console.log(chunk);
}

内存优化

text()json()bytes()arrayBuffer() 这样的方法在可能时避免在内存中复制字符串或字节。

如果文本恰好是 ASCII,Bun 直接将字符串传输到 JavaScriptCore(引擎)而无需转码且在内存中不复制字符串。当你使用 .bytes().arrayBuffer() 时,它也会避免在内存中复制字节。

这些辅助方法不仅简化了 API,还使其更快。

写入和上传文件到 S3

写入 S3 同样简单。

ts
// 写入字符串(替换文件)
await s3file.write("Hello World!");

// 写入 Buffer(替换文件)
await s3file.write(Buffer.from("Hello World!"));

// 写入 Response(替换文件)
await s3file.write(new Response("Hello World!"));

// 写入并指定内容类型
await s3file.write(JSON.stringify({ name: "John", age: 30 }), {
  type: "application/json",
});

// 使用写入器(流式)
const writer = s3file.writer({ type: "application/json" });
writer.write("Hello");
writer.write(" World!");
await writer.end();

// 使用 Bun.write 写入
await Bun.write(s3file, "Hello World!");

处理大文件(流)

Bun 自动处理大文件的多部分上传并提供流式功能。适用于本地文件的 API 也适用于 S3 文件。

ts
// 写入大文件
const bigFile = Buffer.alloc(10 * 1024 * 1024); // 10MB
const writer = s3file.writer({
  // 网络错误时自动重试最多 3 次
  retry: 3,

  // 一次排队最多 10 个请求
  queueSize: 10,

  // 以 5 MB 块上传
  partSize: 5 * 1024 * 1024,
});
for (let i = 0; i < 10; i++) {
  writer.write(bigFile);
  await writer.flush();
}
await writer.end();

预签名 URL

当你的生产服务需要让用户上传文件到你的服务器时,让用户直接上传到 S3 通常比你的服务器作为中介更可靠。

为了促进这一点,你可以为 S3 文件预签名 URL。这会生成一个带签名的 URL,允许用户安全地将该特定文件上传到 S3,而无需暴露你的凭证或授予他们对你的存储桶的不必要访问权限。

默认行为是生成一个 GET URL,在 24 小时后过期。Bun 尝试从文件扩展名推断内容类型。如果无法推断,则默认为 application/octet-stream

ts
import { s3 } from "bun";

// 生成 24 小时后过期的预签名 URL(默认)
const download = s3.presign("my-file.txt"); // GET, text/plain, 24 小时后过期

const upload = s3.presign("my-file", {
  expiresIn: 3600, // 1 小时
  method: "PUT",
  type: "application/json", // 没有扩展名用于推断,所以我们可以指定内容类型为 JSON
});

// 你可以在文件引用上调用 .presign(),但避免这样做
// 除非你已经有引用(以避免内存使用)。
const myFile = s3.file("my-file.txt");
const presignedFile = myFile.presign({
  expiresIn: 3600, // 1 小时
});

设置 ACL

要在预签名 URL 上设置 ACL(访问控制列表),传递 acl 选项:

ts
const url = s3file.presign({
  acl: "public-read",
  expiresIn: 3600,
});

你可以传递以下任何 ACL:

ACL说明
"public-read"对象可由公众读取。
"private"对象仅可由存储桶所有者读取。
"public-read-write"对象可由公众读取和写入。
"authenticated-read"对象可由存储桶所有者和经过身份验证的用户读取。
"aws-exec-read"对象可由发出请求的 AWS 账户读取。
"bucket-owner-read"对象可由存储桶所有者读取。
"bucket-owner-full-control"对象可由存储桶所有者读取和写入。
"log-delivery-write"对象可由用于日志交付的 AWS 服务写入。

过期 URL

要为预签名 URL 设置过期时间,传递 expiresIn 选项。

ts
const url = s3file.presign({
  // 秒
  expiresIn: 3600, // 1 小时

  // 访问控制列表
  acl: "public-read",

  // HTTP 方法
  method: "PUT",
});

method

要为预签名 URL 设置 HTTP 方法,传递 method 选项。

ts
const url = s3file.presign({
  method: "PUT",
  // method: "DELETE",
  // method: "GET",
  // method: "HEAD",
  // method: "POST",
  // method: "PUT",
});

new Response(S3File)

要快速将用户重定向到 S3 文件的预签名 URL,将 S3File 实例作为主体传递给 Response 对象。

这将自动将用户重定向到 S3 文件的预签名 URL,为你节省将文件下载到服务器并发送回用户的内存、时间和带宽成本。

ts
const response = new Response(s3file);
console.log(response);
txt
Response (0 KB) {
  ok: false,
  url: "",
  status: 302,
  statusText: "",
  headers: Headers {
    "location": "https://<account-id>.r2.cloudflarestorage.com/...",
  },
  redirected: true,
  bodyUsed: false
}

支持 S3 兼容服务

Bun 的 S3 实现适用于任何 S3 兼容存储服务。只需指定适当的端点:

将 Bun 的 S3Client 与 AWS S3 一起使用

AWS S3 是默认选项。对于 AWS S3,你也可以传递 region 选项而不是 endpoint 选项。

ts
import { S3Client } from "bun";

// AWS S3
const s3 = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // region: "us-east-1",
});

将 Bun 的 S3Client 与 Google Cloud Storage 一起使用

要将 Bun 的 S3 客户端与 Google Cloud Storage 一起使用,在 S3Client 构造函数中将 endpoint 设置为 "https://storage.googleapis.com"

ts
import { S3Client } from "bun";

// Google Cloud Storage
const gcs = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  endpoint: "https://storage.googleapis.com",
});

将 Bun 的 S3Client 与 Cloudflare R2 一起使用

要将 Bun 的 S3 客户端与 Cloudflare R2 一起使用,在 S3Client 构造函数中将 endpoint 设置为 R2 端点。R2 端点包括你的账户 ID。

ts
import { S3Client } from "bun";

// CloudFlare R2
const r2 = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  endpoint: "https://<account-id>.r2.cloudflarestorage.com",
});

将 Bun 的 S3Client 与 DigitalOcean Spaces 一起使用

要将 Bun 的 S3 客户端与 DigitalOcean Spaces 一起使用,在 S3Client 构造函数中将 endpoint 设置为 DigitalOcean Spaces 端点。

ts
import { S3Client } from "bun";

const spaces = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  // region: "nyc3",
  endpoint: "https://<region>.digitaloceanspaces.com",
});

将 Bun 的 S3Client 与 MinIO 一起使用

要将 Bun 的 S3 客户端与 MinIO 一起使用,在 S3Client 构造函数中将 endpoint 设置为 MinIO 运行的 URL。

ts
import { S3Client } from "bun";

const minio = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",

  // 确保使用正确的端点 URL
  // 在生产环境中可能不是 localhost!
  endpoint: "http://localhost:9000",
});

将 Bun 的 S3Client 与 supabase 一起使用

要将 Bun 的 S3 客户端与 supabase 一起使用,在 S3Client 构造函数中将 endpoint 设置为 supabase 端点。supabase 端点包括你的账户 ID 和 /storage/v1/s3 路径。确保在 supabase 仪表板中的 https://supabase.com/dashboard/project/<account-id>/settings/storage 上启用通过 S3 协议连接,并在同一部分设置区域。

ts
import { S3Client } from "bun";

const supabase = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  region: "us-west-1",
  endpoint: "https://<account-id>.supabase.co/storage/v1/s3/storage",
});

将 Bun 的 S3Client 与 S3 虚拟主机风格端点一起使用

使用 S3 虚拟主机风格端点时,你需要将 virtualHostedStyle 选项设置为 true

NOTE

- 如果你不指定端点,Bun 将自动使用提供的区域和存储桶确定 AWS S3 端点。 - 如果未指定区域,Bun 默认为 us-east-1。 - 如果你显式提供端点,则不需要指定存储桶名称。
ts
import { S3Client } from "bun";

// 从区域和存储桶推断 AWS S3 端点
const s3 = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  virtualHostedStyle: true, 
  // endpoint: "https://my-bucket.s3.us-east-1.amazonaws.com",
  // region: "us-east-1",
});

// AWS S3
const s3WithEndpoint = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  endpoint: "https://<bucket-name>.s3.<region>.amazonaws.com",
  virtualHostedStyle: true, 
});

// Cloudflare R2
const r2WithEndpoint = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  endpoint: "https://<bucket-name>.<account-id>.r2.cloudflarestorage.com",
  virtualHostedStyle: true, 
});

凭证

凭证是使用 S3 最困难的部分之一,我们已尽力使其尽可能简单。默认情况下,Bun 从以下环境变量读取凭证。

选项名称环境变量
accessKeyIdS3_ACCESS_KEY_ID
secretAccessKeyS3_SECRET_ACCESS_KEY
regionS3_REGION
endpointS3_ENDPOINT
bucketS3_BUCKET
sessionTokenS3_SESSION_TOKEN

如果 S3_* 环境变量未设置,Bun 还将检查上述每个选项的 AWS_* 环境变量。

选项名称回退环境变量
accessKeyIdAWS_ACCESS_KEY_ID
secretAccessKeyAWS_SECRET_ACCESS_KEY
regionAWS_REGION
endpointAWS_ENDPOINT
bucketAWS_BUCKET
sessionTokenAWS_SESSION_TOKEN

这些环境变量在初始化时从 .env 文件 或进程环境中读取(process.env 不用于此)。

这些默认值会被你传递给 s3.file(credentials)new Bun.S3Client(credentials) 或任何接受凭证的方法的选项覆盖。因此,例如,如果你为不同的存储桶使用相同的凭证,你可以在 .env 文件中设置一次凭证,然后传递 bucket: "my-bucket"s3.file() 函数而无需再次指定所有凭证。

S3Client 对象

当你不使用环境变量或使用多个存储桶时,你可以创建 S3Client 对象来显式设置凭证。

ts
import { S3Client } from "bun";

const client = new S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // sessionToken: "..."
  endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
  // endpoint: "http://localhost:9000", // MinIO
});

// 使用 Response 写入
await file.write(new Response("Hello World!"));

// 预签名 URL
const url = file.presign({
  expiresIn: 60 * 60 * 24, // 1 天
  acl: "public-read",
});

// 删除文件
await file.delete();

S3Client.prototype.write

要将文件上传或写入 S3,在 S3Client 实例上调用 write

ts
const client = new Bun.S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  endpoint: "https://s3.us-east-1.amazonaws.com",
  bucket: "my-bucket",
});

await client.write("my-file.txt", "Hello World!");
await client.write("my-file.txt", new Response("Hello World!"));

// 等同于
// await client.file("my-file.txt").write("Hello World!");

S3Client.prototype.delete

要从 S3 删除文件,在 S3Client 实例上调用 delete

ts
const client = new Bun.S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
});

await client.delete("my-file.txt");
// 等同于
// await client.file("my-file.txt").delete();

S3Client.prototype.exists

要检查文件是否存在于 S3 中,在 S3Client 实例上调用 exists

ts
const client = new Bun.S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
});

const exists = await client.exists("my-file.txt");
// 等同于
// const exists = await client.file("my-file.txt").exists();

S3File

S3File 实例通过调用 S3Client 实例方法或 s3.file() 函数创建。像 Bun.file() 一样,S3File 实例是惰性的。它们不一定指创建时存在的东西。这就是为什么所有不涉及网络请求的方法都是完全同步的。

ts
interface S3File extends Blob {
  slice(start: number, end?: number): S3File;
  exists(): Promise<boolean>;
  unlink(): Promise<void>;
  presign(options: S3Options): string;
  text(): Promise<string>;
  json(): Promise<any>;
  bytes(): Promise<Uint8Array>;
  arrayBuffer(): Promise<ArrayBuffer>;
  stream(options: S3Options): ReadableStream;
  write(
    data: string | Uint8Array | ArrayBuffer | Blob | ReadableStream | Response | Request,
    options?: BlobPropertyBag,
  ): Promise<number>;

  exists(options?: S3Options): Promise<boolean>;
  unlink(options?: S3Options): Promise<void>;
  delete(options?: S3Options): Promise<void>;
  presign(options?: S3Options): string;

  stat(options?: S3Options): Promise<S3Stat>;
  /**
   * 大小不同步可用,因为它需要网络请求。
   *
   * @deprecated 使用 `stat()` 代替。
   */
  size: NaN;

  // ... 为简洁起见省略更多
}

Bun.file() 一样,S3File 扩展了 Blob,因此 Blob 上可用的所有方法也可用于 S3File。从本地文件读取数据的 API 也适用于从 S3 读取数据。

方法输出
await s3File.text()string
await s3File.bytes()Uint8Array
await s3File.json()JSON
await s3File.stream()ReadableStream
await s3File.arrayBuffer()ArrayBuffer

这意味着将 S3File 实例与 fetch()Response 和其他接受 Blob 实例的 Web API 一起使用可以直接工作。

使用 slice 部分读取

要读取文件的部分范围,你可以使用 slice 方法。

ts
const partial = s3file.slice(0, 1024);

// 将部分范围读取为 Uint8Array
const bytes = await partial.bytes();

// 将部分范围读取为字符串
const text = await partial.text();

在内部,这通过使用 HTTP Range 头仅请求你想要的字节。这个 slice 方法与 Blob.prototype.slice 相同。

从 S3 删除文件

要从 S3 删除文件,你可以使用 delete 方法。

ts
await s3file.delete();
// await s3File.unlink();

deleteunlink 相同。

错误码

当 Bun 的 S3 API 抛出错误时,它将具有与以下值之一匹配的 code 属性:

  • ERR_S3_MISSING_CREDENTIALS
  • ERR_S3_INVALID_METHOD
  • ERR_S3_INVALID_PATH
  • ERR_S3_INVALID_ENDPOINT
  • ERR_S3_INVALID_SIGNATURE
  • ERR_S3_INVALID_SESSION_TOKEN

当 S3 对象存储服务返回错误(即不是 Bun)时,它将是 S3Error 实例(名称为 "S3Error"Error 实例)。

S3Client 静态方法

S3Client 类提供几种用于与 S3 交互的静态方法。

S3Client.write(静态)

要将数据直接写入存储桶中的路径,你可以使用 S3Client.write 静态方法。

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

// 写入字符串
await S3Client.write("my-file.txt", "Hello World");

// 使用类型写入 JSON
await S3Client.write("data.json", JSON.stringify({ hello: "world" }), {
  ...credentials,
  type: "application/json",
});

// 从 fetch 写入
const res = await fetch("https://example.com/data");
await S3Client.write("data.bin", res, credentials);

// 使用 ACL 写入
await S3Client.write("public.html", html, {
  ...credentials,
  acl: "public-read",
  type: "text/html",
});

这等同于调用 new S3Client(credentials).write("my-file.txt", "Hello World")

S3Client.presign(静态)

要为 S3 文件生成预签名 URL,你可以使用 S3Client.presign 静态方法。

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

const url = S3Client.presign("my-file.txt", {
  ...credentials,
  expiresIn: 3600,
});

这等同于调用 new S3Client(credentials).presign("my-file.txt", { expiresIn: 3600 })

S3Client.list(静态)

要列出存储桶中的一些或全部(最多 1,000 个)对象,你可以使用 S3Client.list 静态方法。

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

// 列出存储桶中的(最多)1000 个对象
const allObjects = await S3Client.list(null, credentials);

// 列出 `uploads/` 前缀下的(最多)500 个对象,每个对象带有所有者字段
const uploads = await S3Client.list({
  prefix: 'uploads/',
  maxKeys: 500,
  fetchOwner: true,
}, credentials);

// 检查是否有更多结果可用
if (uploads.isTruncated) {
  // 列出 `uploads/` 前缀下的下一批对象
  const moreUploads = await S3Client.list({
    prefix: 'uploads/',
    maxKeys: 500,
    startAfter: uploads.contents!.at(-1).key
    fetchOwner: true,
  }, credentials);
}

这等同于调用 new S3Client(credentials).list()

S3Client.exists(静态)

要检查 S3 文件是否存在,你可以使用 S3Client.exists 静态方法。

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

const exists = await S3Client.exists("my-file.txt", credentials);

相同的方法也适用于 S3File 实例。

ts
import { s3 } from "bun";

const s3file = s3.file("my-file.txt", {
  // ...credentials,
});

const exists = await s3file.exists();

S3Client.size(静态)

要快速检查 S3 文件的大小而无需下载它,你可以使用 S3Client.size 静态方法。

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

const bytes = await S3Client.size("my-file.txt", credentials);

这等同于调用 new S3Client(credentials).size("my-file.txt")

S3Client.stat(静态)

要获取 S3 文件的大小、etag 和其他元数据,你可以使用 S3Client.stat 静态方法。

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

const stat = await S3Client.stat("my-file.txt", credentials);
txt
{
  etag: "\"7a30b741503c0b461cc14157e2df4ad8\"",
  lastModified: 2025-01-07T00:19:10.000Z,
  size: 1024,
  type: "text/plain;charset=utf-8",
}

S3Client.delete(静态)

要删除 S3 文件,你可以使用 S3Client.delete 静态方法。

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
};

await S3Client.delete("my-file.txt", credentials);
// 等同于
// await new S3Client(credentials).delete("my-file.txt");

// S3Client.unlink 是 S3Client.delete 的别名
await S3Client.unlink("my-file.txt", credentials);

s3:// 协议

为了更容易对本地文件和 S3 文件使用相同的代码,fetchBun.file() 中支持 s3:// 协议。

ts
const response = await fetch("s3://my-bucket/my-file.txt");
const file = Bun.file("s3://my-bucket/my-file.txt");

你还可以将 s3 选项传递给 fetchBun.file 函数。

ts
const response = await fetch("s3://my-bucket/my-file.txt", {
  s3: {
    accessKeyId: "your-access-key",
    secretAccessKey: "your-secret-key",
    endpoint: "https://s3.us-east-1.amazonaws.com",
  },
  headers: {
    range: "bytes=0-1023",
  },
});

UTF-8、UTF-16 和 BOM(字节顺序标记)

ResponseBlob 一样,S3File 默认假设 UTF-8 编码。

当在 S3File 上调用 text()json() 方法之一时:

  • 当检测到 UTF-16 字节顺序标记 (BOM) 时,它将被视为 UTF-16。JavaScriptCore 原生支持 UTF-16,因此它跳过 UTF-8 转码过程(并剥离 BOM)。这主要是好的,但它确实意味着如果你的 UTF-16 字符串中有无效的代理对字符,它们将传递给 JavaScriptCore(与源代码相同)。
  • 当检测到 UTF-8 BOM 时,它在字符串传递给 JavaScriptCore 之前被剥离,无效的 UTF-8 码点被替换为 Unicode 替换字符 (\uFFFD)。
  • 不支持 UTF-32。

Bun学习网由www.bunjs.com.cn整理维护