瀏覽器 File API 深度解析:本機檔案處理的隱私架構

技术架构(更新於 2026年5月20日)

瀏覽器檔案處理的演進

早期 Web 應用處理檔案依賴 <input type="file"> + 表單上傳到伺服器。2010 年後,HTML5 File API 讓 JavaScript 可以直接讀取使用者本機檔案——無需上傳

這一變革催生了「瀏覽器本機工具」品類:PDF 編輯、圖片壓縮、影片轉碼全部在使用者裝置上完成,檔案永不離開瀏覽器。


核心 API 層次

File (使用者選擇的檔案,含 name/size/type/lastModified)
  ↓ extends
Blob (二進位大型物件,含 size/type)
  ↓ 讀取為
ArrayBuffer (原始位元組,適合 WASM/加密)
  ↓ 或
Text (UTF-8 字串,適合 JSON/CSV)
  ↓ 或
Data URL (Base64 編碼,適合內嵌展示)
  ↓ 輸出為
Blob → Object URL → 下載 / 預覽

File vs Blob

// File 是 Blob 的子類別,多了檔名和修改時間
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', (e) => {
  const file: File = e.target.files[0];
  console.log(file.name);         // "report.pdf"
  console.log(file.size);         // 2048576 (bytes)
  console.log(file.type);         // "application/pdf"
  console.log(file.lastModified); // 1717200000000
});

讀取方式對比

方法 回傳類型 適用場景 記憶體特點
file.text() string JSON、CSV、文字 全量載入
file.arrayBuffer() ArrayBuffer PDF、圖片、WASM 輸入 全量載入
file.stream() ReadableStream 大檔案串流處理 分塊讀取
FileReader.readAsDataURL() Data URL 圖片預覽 Base64 膨脹 33%

工具庫的策略:< 50MB 用 arrayBuffer(),> 50MB 用 stream() 分塊處理


Object URL:零拷貝預覽與下載

// 建立暫時 URL(記憶體參考,非磁碟檔案)
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);

// 用於預覽
iframe.src = url;

// 用於下載
const a = document.createElement('a');
a.href = url;
a.download = 'merged.pdf';
a.click();

// ⚠️ 必須釋放,否則記憶體洩漏
URL.revokeObjectURL(url);

Object URL 是 blob: 協定的特殊 URL,指向記憶體中的 Blob 物件。每個未 revoke 的 URL 都會佔用記憶體——批次處理時必須及時釋放。


拖曳上傳的實作

const dropZone = document.getElementById('drop-zone');

dropZone.addEventListener('dragover', (e) => {
  e.preventDefault(); // 必須阻止,否則瀏覽器會開啟檔案
  e.dataTransfer.dropEffect = 'copy';
});

dropZone.addEventListener('drop', (e) => {
  e.preventDefault();
  const files = Array.from(e.dataTransfer.files);
  const pdfs = files.filter(f => f.type === 'application/pdf');
  processFiles(pdfs);
});

DataTransfer.files 回傳 FileList,與 <input type="file"> 的 files 屬性完全相容。


隱私架構:為什麼本機處理更安全

傳統線上工具的資料流

使用者檔案 → HTTPS 上傳 → 伺服器處理 → 下載結果 → 伺服器刪除(聲稱)
                                    ↑
                              可能被快取、記錄、備份

工具庫的資料流

使用者檔案 → File API 讀取 → 瀏覽器記憶體/WASM 處理 → Blob 下載
                ↑
          永不離開使用者裝置
對比維度 伺服器處理 瀏覽器本機處理
檔案傳輸 需要上傳+下載 零網路傳輸
隱私風險 伺服器可能留存 關閉分頁即清除
速度 受網速限制 僅受 CPU 限制
檔案大小 通常 10-50MB 限制 受裝置記憶體限制
離線可用 是(PWA 快取後)

Streams API:處理超大檔案

對於超過 500MB 的影片檔案,全量載入到 ArrayBuffer 可能導致 OOM:

async function processLargeFile(file: File) {
  const stream = file.stream();
  const reader = stream.getReader();
  const chunks: Uint8Array[] = [];

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    chunks.push(value); // 每次只載入一個 chunk(通常 64KB)
  }

  const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
  const result = new Uint8Array(totalLength);
  let offset = 0;
  for (const chunk of chunks) {
    result.set(chunk, offset);
    offset += chunk.length;
  }
  return result;
}

FFmpeg.wasm 支援從 Uint8Array 寫入虛擬檔案系統,配合 Streams 可以處理數 GB 的影片——只要裝置記憶體足夠。


檔案類型偵測:MIME vs 魔數

File.type 來自作業系統副檔名對應,不可靠

// ❌ 使用者可以將 .exe 重新命名為 .pdf
file.type === 'application/pdf' // 可能為 true,但實際不是 PDF

// ✅ 讀取檔案頭魔數
const header = new Uint8Array(await file.slice(0, 5).arrayBuffer());
const isPdf = header[0] === 0x25 && header[1] === 0x50 &&
              header[2] === 0x44 && header[3] === 0x46; // %PDF

工具庫的 檔案類型嗅探工具 就是基於魔數偵測,比副檔名更準確。


下載最佳實務

觸發下載

function downloadBlob(blob: Blob, filename: string) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

批次下載:ZIP 打包

多個檔案不應逐個觸發下載(瀏覽器會阻止彈出視窗)。工具庫使用 JSZip 將多個結果打包為一個 ZIP:

PDF 轉圖片 → 50 張 PNG → JSZip 打包 → 下載 images.zip

總結

File API 是瀏覽器本機工具的技術基石。理解 File → Blob → ArrayBuffer 的轉換鏈、Object URL 的生命週期管理、以及 Streams 的大檔案處理策略,是建構隱私優先 Web 工具的核心能力。工具庫 200+ 工具全部基於這套架構,實現了「檔案不上傳、處理在本機、關閉即清除」的隱私承諾。

本站提供瀏覽器本地工具,免註冊即可試用 →

#File API#Blob#ArrayBuffer#隐私#本地处理