生產服務器通常從本地文件系統讀取、上傳和寫入文件到 S3 兼容的對象存儲服務,而不是本地文件系統。歷史上,這意味著你在開發中使用的本地文件系統 API 不能在生產中使用。當你使用 Bun 時,情況就不同了。
Bun 的 S3 API 很快
Bun 提供快速、原生的綁定用於與 S3 兼容的對象存儲服務交互。Bun 的 S3 API 設計簡單,感覺類似於 fetch 的 Response 和 Blob API(就像 Bun 的本地文件系統 API 一樣)。
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.S3Client 和 Bun.s3
Bun.s3 等同於 new Bun.S3Client(),依賴環境變量獲取憑證。
要顯式設置憑證,將它們傳遞給 Bun.S3Client 構造函數。
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 上文件的惰性引用
const s3file: S3File = client.file("123.json");像 Bun.file(path) 一樣,S3Client 的 file 方法是同步的。在調用依賴網絡請求的方法之前,它不會進行任何網絡請求。
從 S3 讀取文件
如果你使用過 fetch API,你熟悉 Response 和 Blob API。S3File 擴展了 Blob。適用於 Blob 的方法也適用於 S3File。
// 將 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 同樣簡單。
// 寫入字符串(替換文件)
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 文件。
// 寫入大文件
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。
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 選項:
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 選項。
const url = s3file.presign({
// 秒
expiresIn: 3600, // 1 小時
// 訪問控制列表
acl: "public-read",
// HTTP 方法
method: "PUT",
});method
要為預簽名 URL 設置 HTTP 方法,傳遞 method 選項。
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,為你節省將文件下載到服務器並發送回用戶的內存、時間和帶寬成本。
const response = new Response(s3file);
console.log(response);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 選項。
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"。
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。
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 端點。
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。
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 協議連接,並在同一部分設置區域。
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。 - 如果你顯式提供端點,則不需要指定存儲桶名稱。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 從以下環境變量讀取憑證。
| 選項名稱 | 環境變量 |
|---|---|
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 文件中設置一次憑證,然後傳遞 bucket: "my-bucket" 給 s3.file() 函數而無需再次指定所有憑證。
S3Client 對象
當你不使用環境變量或使用多個存儲桶時,你可以創建 S3Client 對象來顯式設置憑證。
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。
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。
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。
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 實例是惰性的。它們不一定指創建時存在的東西。這就是為什麼所有不涉及網絡請求的方法都是完全同步的。
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 方法。
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 方法。
await s3file.delete();
// await s3File.unlink();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
當 S3 對象存儲服務返回錯誤(即不是 Bun)時,它將是 S3Error 實例(名稱為 "S3Error" 的 Error 實例)。
S3Client 靜態方法
S3Client 類提供幾種用於與 S3 交互的靜態方法。
S3Client.write(靜態)
要將數據直接寫入存儲桶中的路徑,你可以使用 S3Client.write 靜態方法。
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 靜態方法。
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 靜態方法。
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 靜態方法。
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 實例。
import { s3 } from "bun";
const s3file = s3.file("my-file.txt", {
// ...credentials,
});
const exists = await s3file.exists();S3Client.size(靜態)
要快速檢查 S3 文件的大小而無需下載它,你可以使用 S3Client.size 靜態方法。
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 靜態方法。
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);{
etag: "\"7a30b741503c0b461cc14157e2df4ad8\"",
lastModified: 2025-01-07T00:19:10.000Z,
size: 1024,
type: "text/plain;charset=utf-8",
}S3Client.delete(靜態)
要刪除 S3 文件,你可以使用 S3Client.delete 靜態方法。
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 文件使用相同的代碼,fetch 和 Bun.file() 中支持 s3:// 協議。
const response = await fetch("s3://my-bucket/my-file.txt");
const file = Bun.file("s3://my-bucket/my-file.txt");你還可以將 s3 選項傳遞給 fetch 和 Bun.file 函數。
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() 方法之一時:
- 當檢測到 UTF-16 字節順序標記 (BOM) 時,它將被視為 UTF-16。JavaScriptCore 原生支持 UTF-16,因此它跳過 UTF-8 轉碼過程(並剝離 BOM)。這主要是好的,但它確實意味著如果你的 UTF-16 字符串中有無效的代理對字符,它們將傳遞給 JavaScriptCore(與源代碼相同)。
- 當檢測到 UTF-8 BOM 時,它在字符串傳遞給 JavaScriptCore 之前被剝離,無效的 UTF-8 碼點被替換為 Unicode 替換字符 (
\uFFFD)。 - 不支持 UTF-32。