執行環境套件管理器打包器測試執行器程式碼檢查格式化指南參考部落格意見回饋

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。