Bun 的 S3 API 很快

左:Bun v1.1.44。右:Node.js v23.6.0
Response 和 Blob API(比如 Bun 的本地文件系统 API)。
- AWS S3
- Cloudflare R2
- DigitalOcean Spaces
- MinIO
- Backblaze B2
- …以及其他任何兼容 S3 的存储服务
基本用法
有多种方式可与 Bun 的 S3 API 进行交互。Bun.S3Client & Bun.s3
Bun.s3 等同于 new Bun.S3Client(),依赖环境变量中的凭据。
若需显式设置凭据,可通过构造函数传递给 Bun.S3Client。
处理 S3 文件
S3Client 中的 file 方法返回对 S3 上文件的 惰性引用。
Bun.file(path),S3Client 的 file 方法是同步的。直到调用依赖网络请求的方法前,它不会进行任何网络请求。
从 S3 读取文件
如果你用过fetch API,就对 Response 和 Blob API 非常熟悉。S3File 继承自 Blob。所有适用于 Blob 的方法也适用于 S3File。
内存优化
像text()、json()、bytes() 或 arrayBuffer() 这样的方法会尽量避免在内存中复制字符串或字节。
如果文本恰好是 ASCII,Bun 会直接将字符串传输给 JavaScriptCore(引擎),无需转码,也不会复制字符串副本。使用 .bytes() 或 .arrayBuffer() 时,也会避免复制字节。
这些辅助方法不仅让 API 更简洁,还提高了速度。
写入与上传文件至 S3
写入 S3 同样简单。处理大文件(流)
Bun 会自动处理大文件的分块上传,并支持流式写入。本地文件适用的 API 同样适用于 S3 文件。预签名 URL
当你的生产服务需要允许用户上传文件时,通常让用户直接上传到 S3 比你的服务器作为中介更可靠。 为此,你可以为 S3 文件预签名 URL。此 URL 包含签名,允许用户安全地上传指定文件到 S3,而不会暴露你的凭据或授予他们对桶的额外访问权限。 默认情况下,预签名 URL 是一个 24 小时后过期的GET 请求。Bun 会尝试根据文件扩展名推断内容类型。如果不能推断,默认使用 application/octet-stream。
设置 ACL
要为预签名 URL 设置 ACL(访问控制列表),传入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 选项。
method
设置预签名 URL 的 HTTP 方法,传入 method 选项。
new Response(S3File)
要快速重定向用户到 S3 文件的预签名 URL,可以将一个 S3File 实例作为响应体传给 Response。
这会自动将用户重定向到该预签名 URL,节省了服务器下载文件再发送给用户的内存、时间和带宽开销。
对兼容 S3 服务的支持
Bun 的 S3 实现支持任何兼容 S3 的存储服务。只需指定对应的 endpoint:使用 Bun 的 S3Client 连接 AWS S3
AWS S3 是默认选项。你可传入region 代替 endpoint 以使用 AWS S3。
使用 Bun 的 S3Client 连接 Google Cloud Storage
使用 Bun 的 S3 client 连接 Google Cloud Storage,在S3Client 构造器中将 endpoint 设置为 "https://storage.googleapis.com"。
使用 Bun 的 S3Client 连接 Cloudflare R2
使用 Bun 的 S3 client 连接 Cloudflare R2,在S3Client 构造器中将 endpoint 设置为包含你的账号 ID 的 R2 端点。
使用 Bun 的 S3Client 连接 DigitalOcean Spaces
使用 Bun 的 S3 client 连接 DigitalOcean Spaces,在S3Client 构造器中将 endpoint 设置为对应区域的 DigitalOcean Spaces 端点。
使用 Bun 的 S3Client 连接 MinIO
使用 Bun 的 S3 client 连接 MinIO,在S3Client 构造器中将 endpoint 设置为 MinIO 运行的 URL。
使用 Bun 的 S3Client 连接 supabase
使用 Bun 的 S3 client 连接 supabase,在S3Client 构造器中将 endpoint 设置为包含账号 ID 及 /storage/v1/s3 路径的 supabase 端点。确保在 supabase 控制台 https://supabase.com/dashboard/project/<account-id>/settings/storage 中启用 “Enable connection via S3 protocol”,并设置相应区域。
使用 Bun 的 S3Client 连接 S3 虚拟主机式端点
使用 S3 虚拟主机式端点时,需要将virtualHostedStyle 选项设置为 true。
- 如果不指定 endpoint,Bun 会根据 region 和 bucket 自动确定 AWS S3 端点。
- 如果不指定 region,Bun 默认使用 us-east-1。
- 如果明确指定 endpoint,则无需指定 bucket 名称。
凭据
凭据是使用 S3 中最难处理的部分之一,我们已尽力简化。默认情况下,Bun 读取以下环境变量作为凭据。| 选项名称 | 环境变量 |
|---|---|
accessKeyId | S3_ACCESS_KEY_ID |
secretAccessKey | S3_SECRET_ACCESS_KEY |
region | S3_REGION |
endpoint | S3_ENDPOINT |
bucket | S3_BUCKET |
sessionToken | S3_SESSION_TOKEN |
S3_* 环境变量,Bun 会依次检查对应的 AWS_* 环境变量。
| 选项名称 | 备用环境变量 |
|---|---|
accessKeyId | AWS_ACCESS_KEY_ID |
secretAccessKey | AWS_SECRET_ACCESS_KEY |
region | AWS_REGION |
endpoint | AWS_ENDPOINT |
bucket | AWS_BUCKET |
sessionToken | AWS_SESSION_TOKEN |
.env 文件 或初始化时的进程环境读取(不会使用 process.env)。
你传给 s3.file(credentials)、new Bun.S3Client(credentials) 或其他接收凭据的方法的选项会覆盖默认值。举例来说,如果不同的桶使用相同凭据,你仅需在 .env 中设置凭据一次,然后调用 s3.file() 时只传 bucket: "my-bucket",无需重复所有凭据。
S3Client 对象
当你不使用环境变量或使用多个桶时,可创建 S3Client 对象来显式设置凭据。
S3Client.prototype.write
向 S3 上传或写入文件,可调用 S3Client 实例的 write 方法。
S3Client.prototype.delete
删除 S3 文件,调用 S3Client 实例的 delete 方法。
S3Client.prototype.exists
检查 S3 文件是否存在,调用 S3Client 实例的 exists 方法。
S3File
S3File 实例通过调用 S3Client 的实例方法或 s3.file() 函数创建。类似 Bun.file(),S3File 实例是惰性创建的。它们不一定指向创建时已存在的文件。因此,所有不涉及网络请求的方法都是完全同步的。
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 方法。
Range 头部请求你想要的字节。此 slice 方法与 Blob.prototype.slice 相同。
从 S3 删除文件
要删除 S3 文件,可以使用delete 方法。
delete 与 unlink 同义。
错误代码
当 Bun 的 S3 API 抛出错误时,错误对象会带有code 属性,值为以下之一:
ERR_S3_MISSING_CREDENTIALSERR_S3_INVALID_METHODERR_S3_INVALID_PATHERR_S3_INVALID_ENDPOINTERR_S3_INVALID_SIGNATUREERR_S3_INVALID_SESSION_TOKEN
"S3Error" 的 Error。
S3Client 静态方法
S3Client 类提供了多个静态方法用以操作 S3。
静态方法 S3Client.write
直接写入数据到存储桶的路径,可使用静态方法 S3Client.write。
new S3Client(credentials).write("my-file.txt", "Hello World")。
静态方法 S3Client.presign
生成 S3 文件预签名 URL,可使用静态方法 S3Client.presign。
new S3Client(credentials).presign("my-file.txt", { expiresIn: 3600 })。
静态方法 S3Client.list
列出存储桶中的部分或所有(最多 1000 个)对象,使用静态方法 S3Client.list。
new S3Client(credentials).list()。
静态方法 S3Client.exists
检查 S3 文件是否存在,使用静态方法 S3Client.exists。
S3File 实例。
静态方法 S3Client.size
快速检查 S3 文件大小(无需下载),使用静态方法 S3Client.size。
new S3Client(credentials).size("my-file.txt")。
静态方法 S3Client.stat
获取 S3 文件的大小、etag 和其他元数据,使用静态方法 S3Client.stat。
静态方法 S3Client.delete
删除 S3 文件,使用静态方法 S3Client.delete。
s3:// 协议
为了方便本地文件和 S3 文件复用同样的代码,s3:// 协议在 fetch 和 Bun.file() 中得到支持。
fetch 和 Bun.file 传递 s3 选项。
UTF-8、UTF-16 和 BOM(字节顺序标记)
跟Response 和 Blob 一样,S3File 默认假定为 UTF-8 编码。
调用 S3File 的 text() 或 json() 方法时:
- 如果检测到 UTF-16 字节顺序标记(BOM),则按 UTF-16 处理。JavaScriptCore 原生支持 UTF-16,因此跳过 UTF-8 转码过程(并剥离 BOM)。这通常很好,但如果 UTF-16 字符中有无效代理对,则会传递给 JavaScriptCore(与源码情况相同)。
- 如果检测到 UTF-8 BOM,则在传给 JavaScriptCore 前剥离 BOM,并将无效的 UTF-8 代码点替换为 Unicode 替代字符 (
\uFFFD)。 - 不支持 UTF-32。