跳至主要內容
Skip to content

Blob 與 Base64 的互轉實戰

在前端開發(如 Vue.js 或 React)處理圖片上傳或裁切時,經常需要在 Blob 與 Base64 之間轉換。本篇將提供完整的轉換程式碼,並點出常見的效能陷阱。


一、 情境 A:Blob 轉 Base64

用途

  • 製作圖片預覽 (Preview)
  • 將圖片存入 localStorage(因為 localStorage 只能存字串)
  • 透過 JSON API 傳輸小型圖片

程式碼

javascript
/**
 * 將 Blob (或 File) 轉換為 Base64 字串
 * @param {Blob} blob
 * @returns {Promise<string>}
 */
function blobToBase64(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result); // result 就是 Base64
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}

// 使用範例
async function handleFileSelect(file) {
  const base64String = await blobToBase64(file);
  console.log(base64String);
  // 輸出: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
}

現代替代方案

如果你的目標瀏覽器支援較新的 API,可以使用更簡潔的寫法:

javascript
async function blobToBase64Modern(blob) {
  const buffer = await blob.arrayBuffer();
  const bytes = new Uint8Array(buffer);
  const binary = bytes.reduce(
    (acc, byte) => acc + String.fromCharCode(byte),
    ""
  );
  return `data:${blob.type};base64,${btoa(binary)}`;
}

二、 情境 B:Base64 轉 Blob

用途

  • 圖片裁切後(Canvas 通常吐出 Base64),需要轉回 Blob 以便透過 API 上傳
  • 接收 Base64 格式的圖片資料,需要建立 Blob URL 預覽

程式碼

javascript
/**
 * 將 Base64 字串轉換為 Blob
 * @param {string} base64 - 完整的 Data URL (含 data:image/png;base64, 前綴)
 * @param {string} mimeType - 可選,預設從 Base64 中解析
 * @returns {Blob}
 */
function base64ToBlob(base64, mimeType) {
  // 1. 解析 MIME Type (如果沒有提供)
  if (!mimeType) {
    const match = base64.match(/^data:(.+?);base64,/);
    mimeType = match ? match[1] : "application/octet-stream";
  }

  // 2. 去除開頭的 "data:image/png;base64," 並解碼
  const byteString = atob(base64.split(",")[1]);

  // 3. 建立 ArrayBuffer 並填入資料
  const ab = new ArrayBuffer(byteString.length);
  const ia = new Uint8Array(ab);

  for (let i = 0; i < byteString.length; i++) {
    ia[i] = byteString.charCodeAt(i);
  }

  // 4. 建立並回傳 Blob 物件
  return new Blob([ab], { type: mimeType });
}

// 使用範例
const base64Image = "data:image/png;base64,iVBORw0KGgo...";
const blob = base64ToBlob(base64Image);
console.log(blob); // Blob { size: 1234, type: "image/png" }

三、 效能陷阱與最佳實踐

陷阱 1:大圖片轉 Base64 會造成 UI 卡頓

FileReader.readAsDataURL() 會把整張圖片轉成一個超長字串。如果圖片有 5MB,Base64 字串會變成約 6.7MB,而瀏覽器需要:

  1. 讀取整個檔案到記憶體
  2. 編碼成 Base64 字串
  3. 把字串塞進 DOM(如果你設定給 <img src="...">

這整個過程可能讓 UI 凍結數秒。

解法:使用 Blob URL 做預覽

javascript
// ❌ 不推薦:轉 Base64 做預覽
const base64 = await blobToBase64(largeFile); // 可能卡頓 2-3 秒
imageElement.src = base64;

// ✅ 推薦:使用 Blob URL
const url = URL.createObjectURL(largeFile); // 瞬間完成
imageElement.src = url;

// 記得在不需要時釋放
imageElement.onload = () => {
  URL.revokeObjectURL(url);
};

陷阱 2:忘記釋放 Blob URL

URL.createObjectURL() 會在瀏覽器記憶體中建立一個參照。如果你不主動呼叫 URL.revokeObjectURL(),這個參照會一直佔用記憶體直到頁面關閉。

> **記憶體洩漏**

如果你的應用頻繁建立預覽(例如圖片編輯器),忘記釋放 Blob URL 可能導致記憶體暴增。

javascript
// Vue 3 範例:在元件卸載時清理
import { ref, onUnmounted } from "vue";

const previewUrl = ref(null);

function setPreview(file) {
  // 先釋放舊的
  if (previewUrl.value) {
    URL.revokeObjectURL(previewUrl.value);
  }
  previewUrl.value = URL.createObjectURL(file);
}

onUnmounted(() => {
  if (previewUrl.value) {
    URL.revokeObjectURL(previewUrl.value);
  }
});

四、 完整範例:圖片裁切上傳流程

以下是一個常見的實戰流程:使用者選擇圖片 → 裁切 → 上傳。

javascript
// 假設你使用 cropper.js 或類似的裁切庫

async function handleCropAndUpload(cropper) {
  // 1. 取得裁切後的 Canvas
  const canvas = cropper.getCroppedCanvas({
    width: 800,
    height: 600,
  });

  // 2. Canvas 轉 Blob (比 toDataURL 更有效率)
  const blob = await new Promise((resolve) => {
    canvas.toBlob(resolve, "image/jpeg", 0.9);
  });

  // 3. 建立 FormData 上傳
  const formData = new FormData();
  formData.append("avatar", blob, "cropped-image.jpg");

  // 4. 發送請求
  const response = await fetch("/api/upload", {
    method: "POST",
    body: formData,
  });

  return response.json();
}

> **為什麼用 `canvas.toBlob()` 而不是 `canvas.toDataURL()`?**

  • toDataURL() 回傳 Base64 字串,你還需要再轉成 Blob 才能上傳
  • toBlob() 直接回傳 Blob,省去轉換步驟,效能更好

五、 快速參考表

需求方法注意事項
Blob → Base64FileReader.readAsDataURL()大檔案會卡 UI
Base64 → Blobatob() + Uint8Array + new Blob()記得解析 MIME Type
圖片預覽URL.createObjectURL()用完要 revokeObjectURL()
Canvas → 上傳用 Blobcanvas.toBlob()toDataURL() 效率高

總結

  • Blob → Base64:使用 FileReader,但要注意大檔案的效能問題
  • Base64 → Blob:使用 atob() 解碼 + Uint8Array 建構
  • 圖片預覽:優先使用 URL.createObjectURL(),避免 Base64 的效能開銷
  • Canvas 上傳:使用 canvas.toBlob() 直接取得 Blob

← 上一章:Bit/Byte/0-255 | 返回專題首頁 | 下一章:ArrayBuffer 操作 →