Blob vs Base64 — 同一份資料的兩種面貌
在 Web 開發(特別是處理圖片、檔案上傳)中,Blob 與 Base64 是兩個最常見的名詞。簡單來說,它們是 「同一份資料的兩種不同表現形式」:
- Blob (Binary Large Object):電腦看檔案的方式(二進位、原始資料)
- Base64:為了讓人類或特定傳輸協定方便處理而轉成的「字串」(純文字)
TIP
比喻 想像你打客服電話報訂單編號 A7B3:
- Blob 就像直接快速唸出 「A-7-B-3」。簡短高效,但電話那頭可能聽成「A-7-D-3」或「8-7-B-3」。
- Base64 就像改說 「A-Apple、7、B-Boy、3」。內容變長了,但能確保對方在「只靠聲音」的通道中準確接收,不會出錯。
先講結論
| 你想做的事 | 推薦做法 | 原因 |
|---|---|---|
| 上傳檔案 | FormData.append("file", file) | 保留原始二進位,傳輸量小,後端好接 |
| 顯示本機預覽 | URL.createObjectURL(file) | 不需要把整個檔案轉成字串 |
| 把小圖示寫進 CSS/HTML | Base64 Data URL | 可省一次請求,但只適合小資源 |
| 把圖片放進 JSON | 小圖可 Base64;大圖改 multipart | JSON 沒有 Binary 型別 |
| 長期保存圖片 | 上傳到後端或存 IndexedDB | Blob URL 不是永久網址 |
一、 核心差異比較表
| 特性 | Blob (Binary Large Object) | Base64 / Data URL |
|---|---|---|
| 本質 | 原始二進制資料 | 可放進文字通道的編碼字串 |
| 大小 | 接近原始檔案大小 | 通常約膨脹 33%,Data URL 前綴還會再多一點 |
| 主要用途 | 檔案上傳、下載、影片串流、本機預覽 | 小圖示內嵌、JSON 傳小型檔案、文字欄位保存 |
| 記憶體消耗 | 瀏覽器可用原生檔案物件處理 | 大字串解析、複製與塞入 DOM 都比較吃資源 |
| 渲染方式 | 透過 Blob URL 或直接交給 API | 可直接放入 src,例如 data:image/png;base64,... |
二、 為什麼 Blob 無法直接塞進 HTML/JSON?
這是因為 Blob 是「二進位世界」的居民,而 HTML/CSS/JSON 是「文字世界」的產物。如果不經過轉換,直接把 Blob 硬塞進去,會發生三個致命問題:
1. 語法崩壞 (Syntax Breaking)
HTML、CSS 和 JSON 都是基於 純文字 (Text-based) 的格式,它們依賴特定的「特殊符號」來定義結構:
- HTML 依賴
<和> - CSS 依賴
{和} - JSON 依賴
"(雙引號) 和,(逗號)
Blob (二進制資料) 是由 0 到 255 的位元組(Bytes)組成。在這些二進位數據中,很有可能剛好包含代表 <、" 或 } 的代碼。
WARNING
災難場景 假設你硬要把一張圖片的二進制資料塞進 HTML 的 src 屬性裡:
<img src="...[圖片二進制資料]..." />如果圖片資料裡剛好有一個 byte 是 0x22 (也就是雙引號 "),瀏覽器會以為屬性結束了,導致後面的資料被當成 HTML 標籤解析,造成網頁結構瞬間瓦解。
2. JSON 的序列化限制
JSON (JavaScript Object Notation) 的標準規範極度嚴格,它只支援以下幾種資料型態:
- 字串 (String)
- 數字 (Number)
- 布林值 (Boolean)
- 陣列 (Array)
- 物件 (Object)
- null
JSON 不支援「Binary」型別。
當你嘗試在 JavaScript 中把一個 Blob 物件轉成 JSON 字串時:
const blob = new Blob(["Hello"], { type: "text/plain" });
const json = JSON.stringify({ file: blob });
console.log(json);
// 輸出結果: {"file":{}}結果是空的! 因為 Blob 是一個指向記憶體(或硬碟)的「複雜物件參照」,它不是純資料值。JSON.stringify 不知道該如何把這個「記憶體指針」變成文字,所以直接忽略它。
3. 傳輸協定的字元編碼問題
HTML/CSS/JSON 通常使用 UTF-8 編碼來儲存和傳輸。UTF-8 是針對文字設計的編碼,並非所有的二進位 byte 序列都是合法的 UTF-8 字元。
如果你把原始的 Blob 二進制資料直接當作文字讀取:
- 會出現大量的 亂碼
- 會出現 控制字元(如
NULLbyte,\0)。JavaScript 字串本身可以包含\0,但許多底層語言、舊系統或文字處理流程會把它視為特殊字元,可能造成資料解析錯誤或截斷
三、 那我們該怎麼辦?
因為「水(Blob)」不能直接裝進「紙袋(HTML/JSON)」裡,我們有兩種標準做法:
方法 A:把水結成冰(轉成 Base64)
把二進制資料編碼成文字字串。標準 Base64 主要由 a-z、A-Z、0-9、+、/ 組成,尾端可能有 = 補齊;URL-safe Base64 則常把 +、/ 換成 -、_。
- 優點:可以直接放進 JSON、CSS (
background-image: url(...)) 或 HTML 裡 - 缺點:體積變大 33%,解析耗效能
方法 B:給水一個門牌號碼(Blob URL)
不把資料放進 HTML,而是產生一個連結 blob:http://localhost/...。
const url = URL.createObjectURL(file);
// url = "blob:http://localhost:3000/550e8400-e29b-41d4-a716-446655440000"- 原理:告訴 HTML:「圖片不在這裡,請去記憶體的這個地址找」
- 優點:效能極佳,HTML 依然保持乾淨的文字格式
- 限制:它不是可持久化網址,生命週期受建立它的瀏覽器文件管理,無法存進 JSON 傳給後端後再使用
四、 實戰建議:該選哪一個?
如果是「檔案上傳」:使用 Blob
當你要把使用者選的照片傳給後端 API 時,絕對建議使用 Blob。
- 原因:透過
FormData傳送 Blob 是標準的multipart/form-data格式,伺服器不需要額外解碼 Base64,且傳輸量小 33%
如果是「圖片預覽」:使用 Blob URL
早期大家習慣轉 Base64 做預覽,但現在更推薦使用 Blob URL (Object URL)。
const url = URL.createObjectURL(file);
imageElement.src = url;- 原因:這是瀏覽器內部的指針,幾乎不消耗效能,瞬間產生。Base64 轉換大圖時會造成 UI 卡頓
- 注意:用完記得釋放
URL.revokeObjectURL(url)
Blob URL 適合「這個頁面目前要預覽」的情境。它不適合存進資料庫、傳給後端、貼給別人或期待重新整理後仍可使用。
如果是「極小的圖示」:使用 Base64
- 原因:如果圖片只有 1-2KB,為了省去一次 HTTP Request,可以直接將 Base64 寫死在 CSS 或 HTML 裡
總結
| 情境 | 推薦方案 | 理由 |
|---|---|---|
| 檔案上傳 | Blob + FormData | 傳輸量小、伺服器無需解碼 |
| 圖片預覽 | Blob URL | 效能極佳、瞬間產生 |
| 存入 localStorage | Base64 | localStorage 只能存字串 |
| 極小圖示 (< 2KB) | Base64 | 省一次 HTTP Request |
| JSON API 傳輸圖片 | Base64 或改用 FormData | JSON 不支援 Binary |
- Blob 是為了 效能與傳輸(給機器與網路用的)
- Base64 是為了 方便嵌入與文字化(給 HTML/CSS 或 JSON 用的)
常見誤解
| 誤解 | 正確理解 |
|---|---|
| Blob 不能顯示圖片 | Blob 可以顯示,但通常要先建立 Blob URL |
| Base64 比 Blob 更安全 | Base64 只是編碼,不是加密,也不會自動消毒內容 |
| Blob URL 是永久連結 | Blob URL 只在建立它的瀏覽器環境中有效 |
| JSON 可以直接放 Blob | JSON.stringify(blob) 不會序列化檔案內容 |
練習:驗證你真的懂了
練習 1:JSON 真的放不進 Blob 嗎?
先猜下面會輸出什麼,再貼到瀏覽器 Console 執行:
const blob = new Blob(["Hello"], { type: "text/plain" });
console.log(JSON.stringify({ file: blob }));
console.log(blob.size, blob.type);看答案
JSON.stringify({ file: blob }) 會得到 {"file":{}}。Blob 本身仍然有 size 和 type,但 JSON 不知道如何把檔案內容序列化成文字欄位。
練習 2:Blob URL 是永久網址嗎?
跑這段程式:
const blob = new Blob(["Hello Blob URL"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
console.log(url);
URL.revokeObjectURL(url);觀察 url 長什麼樣子。它看起來像網址,但它只是在目前瀏覽器環境中指向一份 Blob 的臨時參照。
自我檢查
- 如果要上傳 8MB 圖片,會用 Base64 還是
FormData? - Blob URL 適合存進資料庫嗎?
- Base64 是加密嗎?
JSON.stringify(blob)會包含檔案內容嗎?