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

S3

Bun 提供了快速的原生绑定,用于与兼容 S3 的对象存储服务交互。

生产服务器通常读取、上传和写入文件到兼容 S3 的对象存储服务,而不是本地文件系统。历史上这意味着开发时使用的本地文件系统 API 无法在生产环境中使用。但使用 Bun,情况则有所不同。

Bun 的 S3 API 很快

Bun 的 S3 API 很快

Bun 提供了快速的原生绑定,用于与兼容 S3 的对象存储服务交互。其 S3 API 类似于 fetch 的 Response 和 Blob API(就像 Bun 的本地文件系统 API)。

s3.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.S3Client & Bun.s3

Bun.s3 等同于 new Bun.S3Client(),依赖环境变量中的凭据。

若需显式设置凭据,可通过构造函数传递给 Bun.S3Client。

s3.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 上文件的 惰性引用。

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

与 Bun.file(path) 类似,S3Client 的 file 方法是同步的。在调用需要网络请求的方法之前,它不会发起任何网络请求。

从 S3 读取文件

S3File 扩展自 Blob,因此适用于 Blob 的方法同样适用于 S3File。

s3.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() 同样会避免在内存中复制字节。

写入与上传文件至 S3

写入到 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",
});

// 使用内容编码写入(例如用于预压缩数据)
await s3file.write(compressedData, {
  type: "application/json",
  contentEncoding: "gzip",
});

// 使用内容处置写入
await s3file.write(pdfData, {
  type: "application/pdf",
  contentDisposition: 'attachment; filename="report.pdf"',
});

// 使用 writer(流式)
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 文件。

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

  // 同时排队最多 10 个请求
  queueSize: 10,

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

预签名 URL

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

要让用户直接上传到 S3,请为 S3 文件生成预签名 URL。生成预签名 URL 时会创建一个带签名的 URL,允许用户将指定文件上传到 S3,而不会暴露你的凭据,也不会授予他们对存储桶不必要的访问权限。

默认情况下,Bun 会生成一个有效期为 24 小时的 GET URL。

s3.ts
import { s3 } from "bun";

// 生成一个有效期为 24 小时的预签名 URL(默认)
const download = s3.presign("my-file.txt"); // GET,有效期为 24 小时

const upload = s3.presign("my-file", {
  expiresIn: 3600, // 1 小时
  method: "PUT",
  type: "application/json", // 设置预签名 URL 中的响应内容类型
});

// 使用内容处置进行预签名(例如强制使用特定文件名进行下载)
const downloadUrl = s3.presign("report.pdf", {
  expiresIn: 3600,
  contentDisposition: 'attachment; filename="quarterly-report.pdf"',
});

// 如果已有文件引用,也可以调用 .presign(),但应避免频繁这样做
// 以减少内存占用。
const myFile = s3.file("my-file.txt");
const presignedFile = myFile.presign({
  expiresIn: 3600, // 1 小时
});

设置 ACL

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

s3.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 选项。

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

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

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

method

设置预签名 URL 的 HTTP 方法,传入 method 选项。

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

new Response(S3File)

要将用户重定向到 S3 文件的预签名 URL,请将一个 S3File 实例作为 body 传给 Response 对象。

此响应会将用户重定向到 S3 文件的预签名 URL。这样可以避免将文件下载到你的服务器并发送回用户所需的内存、时间和带宽开销。

s3.ts
const response = new Response(s3file);
console.log(response);
Text
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 选项。

s3.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"。

s3.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 设置为包含你的账号 ID 的 R2 端点。

s3.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 端点。

s3.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。

s3.ts
import { S3Client } from "bun";

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

  // 确保使用正确的 endpoint 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 中,开启 Enable connection via S3 protocol,并使用该部分显示的区域。

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",
});

使用 Bun 的 S3Client 连接 S3 虚拟主机式端点

使用虚拟主机式端点时,将 virtualHostedStyle 选项设置为 true。

s3.ts
import { S3Client } from "bun";

// 从 region 和 bucket 推断的 AWS S3 端点
const s3 = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  virtualHostedStyle: true, // [!code ++]
  // endpoint: "https://my-bucket.s3.us-east-1.amazonaws.com",
  // region: "us-east-1",
});

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

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

凭据

默认情况下,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

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

你传递给 s3.file(credentials)、new Bun.S3Client(credentials) 或任何接受凭据的方法的选项会覆盖这些默认值。因此,如果你对不同的存储桶使用相同的凭据,可以在 .env 文件中设置一次凭据,然后只将 bucket: "my-bucket" 传递给 s3.file()。

Bun 会通过 HTTP_PROXY、HTTPS_PROXY、ALL_PROXY 和 NO_PROXY 为 endpoint 选择的代理发送 S3 请求,方式与 fetch 相同。

S3Client 对象

如果你不使用环境变量,或者正在使用多个存储桶,可以创建一个 S3Client 对象来显式设置凭据。

s3.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
});

const file = client.file("my-file.txt");

// Write using a 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 方法。

s3.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 方法。

s3.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 方法。

s3.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

在 S3Client 实例上调用 file() 或调用 s3.file(),会返回一个 S3File。与 Bun.file() 一样,S3File 实例是惰性的:它们在创建时不一定指向实际存在的对象。因此,所有不涉及网络请求的方法都是完全同步的。

Type
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
s3File.stream()ReadableStream
await s3File.arrayBuffer()ArrayBuffer

由于 S3File 继承自 Blob,S3File 实例可与 fetch()、Response 以及其他接受 Blob 实例的 Web API 配合使用。

使用 slice 部分读取

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

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

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

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

在内部,Bun 使用 HTTP Range 标头来请求所需的字节。此 slice 方法与 Blob.prototype.slice 相同。

从 S3 删除文件

要从 S3 删除文件,请使用 delete 方法。

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

delete 与 unlink 同义。

错误代码

当 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 静态方法。

s3.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
};

// Write string
await S3Client.write("my-file.txt", "Hello World", credentials);

// 指定类型写入 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 静态方法。

s3.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 静态方法。

s3.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 个对象,并获取每个对象的 owner 字段
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 静态方法。

s3.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 实例。

s3.ts
import { s3 } from "bun";

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

const exists = await s3file.exists();

静态方法 S3Client.size

要在不下载 S3 文件的情况下检查其大小,请使用 S3Client.size 静态方法。

s3.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 静态方法。

s3.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);
Text
{
  etag: "\"7a30b741503c0b461cc14157e2df4ad8\"",
  lastModified: 2025-01-07T00:19:10.000Z,
  size: 1024,
  type: "text/plain;charset=utf-8",
}

静态方法 S3Client.delete

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

s3.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:// 协议

fetch 和 Bun.file() 支持 s3:// 协议,因此相同的代码可用于本地文件和 S3 文件。

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

你还可以向 fetch 和 Bun.file 传入 s3 选项。

s3.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(字节顺序标记)

跟 Response 和 Blob 一样,S3File 默认假定为 UTF-8 编码。

调用 S3File 的 text() 或 json() 时:

  • 当 Bun 检测到 UTF-16 字节顺序标记(BOM)时,会将数据视为 UTF-16。JavaScriptCore 原生支持 UTF-16,因此 Bun 会跳过 UTF-8 转码步骤(并移除 BOM)。由此产生的一个结果是,UTF-16 字符串中的无效代理项对会直接传递给 JavaScriptCore(与源代码相同)。
  • 当 Bun 检测到 UTF-8 BOM 时,会移除 BOM,并在将字符串传递给 JavaScriptCore 之前,将无效的 UTF-8 码点替换为 Unicode 替换字符(\uFFFD)。
  • 不支持 UTF-32。