一次搞懂 JS 的二進位資料處理:Blob、File、FileReader、ArrayBuffer 到 Buffer 全解析
初探 JS 二進制:基礎概念與關鍵類型
什麼是二進制資料?
在我們的日常生活中,文字、圖片、音樂、甚至影片,對我們來說可能很直觀,但電腦其實看不到這些「豐富的內容」,它只能看到由 0 和 1 組成的數據——這就是所謂的二進制資料。
簡單來說,二進制資料就是電腦世界裡的「原始語言」。無論是你在網頁上點擊下載的文件,還是後端伺服器處理的數據流,這些其實都脫不開二進制資料的範疇。
你可能會想,既然這些由 0 和 1 組成的二進制資料只有電腦本人看得懂,那麼在什麼情況下我們會需要去操作他們呢?以前端來說,以下這些都是常見的需求場景:
- 上傳或下載文件(比如圖片、PDF)。
- 將圖片或音訊處理後傳輸到伺服器。
- 處理流式數據,比如大文件的分塊上傳。
儘管 JavaScript 主要用於處理字符串和 JSON 等高階資料格式,但它其實也提供了一套完整的工具,來處理更底層的二進制資料。
JS 的二進制資料家族
現在,讓我們來看看 JavaScript 提供的這些「工具」。我把它們整理成了一張表,這樣你可以一目了然地了解每個工具的功能和用途:
ArrayBuffer
├─ DataView
└─ TypedArray
├─ Uint8Array
├─ Int16Array
└─ ...
Blob
├─ File
├─ FileList
└─ FileReader
Buffer (Node.js 環境)
| 成員 | 用途與特點 | 常見使用場景 |
|---|---|---|
| ArrayBuffer | 固定大小的二進制緩衝區,是其他類型的基礎。 | 圖片處理、音訊處理、網路數據傳輸 |
| TypedArray | 基於 ArrayBuffer 的結構化數組,支持操作不同類型的數據(如整數或浮點數)。 | 編碼或解碼多媒體數據,例如影像像素或音訊波形 |
| DataView | 提供更靈活的方式操作 ArrayBuffer,可以按需定義數據結構。 | 解析複雜的二進制數據格式,如自定義檔案格式或網路協議 |
| Blob | 儲存二進制數據的文件對象,可以方便地用於文件下載或傳輸。 | 文件上傳、圖像處理、後端資料接收 |
| File | 繼承自 Blob,用於描述用戶選擇的文件,比如通過 <input type="file"> 選擇的檔案。 | 文件上傳 |
| FileList | 表示多個文件的集合,通常來自 <input type="file" multiple>。 | 批量文件上傳 |
| FileReader | 讀取文件內容,支持讀取成多種格式(如文字、ArrayBuffer、DataURL)。 | 預覽文件、讀取文件數據 |
| Buffer | Node.js 環境專屬,用於高效處理二進制數據。 | 網路請求、資料庫數據處理 |
二進制資料的協作關聯圖解

這些工具並非獨立存在,而是相互協作,幫助我們完成不同任務。比如,當你需要上傳一張圖片時:
- 使用者選擇圖片,產生一個
File物件。 - 使用
FileReader將檔案內容讀取為ArrayBuffer。 - 如果需要進一步處理,可以將
ArrayBuffer轉換為TypedArray,用來操作像素資料。 - 最後,將處理後的資料重新封裝成
Blob,供使用者下載或傳輸。
Blob:從檔案到資料流的核心
Blob 的定義與特性
在 JavaScript 世界裡,Blob 是處理二進制文件的核心工具。它的全名是 Binary Large Object,意即「二進位大型物件」。簡單來說,Blob 代表的是一塊不可變的二進制資料,這些資料可能是文字、圖像、音訊甚至影片。這些資料通常是由一個或多個 ArrayBuffer 或 DOMString 組成的。
Blob 是不可修改的。如果需要操作它的內容,必須透過工具如 FileReader 來讀取並生成新的 Blob。
如何創建 Blob?
JavaScript 提供了一個簡單的方法來建立 Blob,使用它的建構函式:
new Blob(blobParts[, options])
- blobParts:
- 可以由
ArrayBuffer、ArrayBufferView、Blob或DOMString組成的Array物件
- 可以由
- options:
type屬性,預設值為空字串"",表示將被放進Blob物件的陣列內容之 MIME 類型。endings屬性,表示包含\n換行字元的字串要如何輸出,預設值為字串"transparent"。
範例:創建一個文字檔案的 Blob
const text = "Hello, Blob!";
const textBlob = new Blob([text], { type: "text/plain" });
console.log(textBlob); // Blob {size: 12, type: "text/plain"}
Blob 的屬性與方法
- 屬性
size: 表示儲存的資料,總共佔了多少位元(byte)。type: 表示儲存的資料格式(MIME type)。
- 方法
slice:用於將一個 Blob 切分成多個子 Blob。
範 例:分割 Blob
const textBlob = new Blob(["Hello, world!"], { type: "text/plain" });
console.log(textBlob.size); // 13 (字元數加空格與符號)
console.log(textBlob.type); // text/plain
const largeBlob = new Blob(["This is a large blob"], { type: "text/plain" });
const slicedBlob = largeBlob.slice(0, 4); // 包含 "This"
console.log(await slicedBlob.text()); // "This"
實戰:Blob 的常見應用
1. 文件下載
在前端中,可以利用 URL.createObjectURL 為 Blob 生成臨時的 URL,並賦值給 <a> 標籤的 href 屬性來實現文件下載。
React 前端示範:
import React from "react";
const DownloadFile: React.FC = () => {
const handleDownload = () => {
const blob = new Blob(["This is a test file."], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "test.txt";
a.click();
URL.revokeObjectURL(url); // 釋放資源
};
return <button onClick={handleDownload}>下載檔案</button>;
};
export default DownloadFile;
2. 圖片顯示
類似文件下載,我們可以將 Blob 的 URL 賦值給圖片的 src 屬性,來顯示圖片。
React 前端示範:
import React, { useState } from "react";
const DisplayImage: React.FC = () => {
const [imageUrl, setImageUrl] = useState<string | null>(null);
const handleUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
const url = URL.createObjectURL(file);
setImageUrl(url);
}
};
return (
<div>
<input type="file" accept="image/*" onChange={handleUpload} />
{imageUrl && <img src={imageUrl} alt="Uploaded" style={{ maxWidth: "100%" }} />}
</div>
);
};
export default DisplayImage;
3. 資源分段上傳
分段上傳 是一種有效解決大文件上傳的方法。我們可以利用 Blob.slice 將文件切成多個片段,然後通過 API 一個個上傳。
Express 後端示範:
假設我們接收前端分段上傳的文件片段,並最終合併成完整文件。
import express, { Request, Response } from "express";
import fs from "fs";
import path from "path";
const app = express();
const UPLOAD_DIR = path.join(__dirname, "uploads");
app.use(express.json());
app.post("/upload", (req: Request, res: Response) => {
const { chunk, filename, index } = req.body;
const chunkPath = path.join(UPLOAD_DIR, `${filename}-${index}`);
fs.writeFileSync(chunkPath, Buffer.from(chunk, "base64")); // 將片段保存
res.status(200).send("Chunk uploaded");
});
app.post("/merge", (req: Request, res: Response) => {
const { filename, totalChunks } = req.body;
const filePath = path.join(UPLOAD_DIR, filename);
const writeStream = fs.createWriteStream(filePath);
for (let i = 0; i < totalChunks; i++) {
const chunkPath = path.join(UPLOAD_DIR, `${filename}-${i}`);
const data = fs.readFileSync(chunkPath);
writeStream.write(data);
fs.unlinkSync(chunkPath); // 刪除片段
}
writeStream.end();
res.status(200).send("File merged");
});
app.listen(3000, () => console.log("Server running on http://localhost:3000"));
File 與 FileList:瀏覽器中的檔案處理
File:特殊的Blob物件
File 是 JS 中專門用來處理文件的類型,它繼承自 Blob,因此也具備 Blob 的所有特性與方法。除了 Blob 的基本屬性(size 和 type)外增加了一些專門用於描述文件的屬性,如:
name:文件的名稱(不包含路徑)。lastModified:最後修改的時間戳,表示文件的上次修改日期(毫秒)。lastModifiedDate(已過時):用來表示最後修改的日期(舊版瀏覽器支持)。webkitRelativePath:相對路徑,當使用多層文件夾的上傳時,這個屬性會保存文件的相對路徑。
FileList:多個文件的集合
FileList 是一個類似陣列的物件,它用來儲存多個 File 物件。典型的應用場景是當使用者使用 <input> 或拖放文件時,瀏覽器會返回一個 FileList,我們可以通過遍歷這個 FileList 來獲取所有的文件。
-
使用
<input type="file" multiple>取得 FileList如果使用者選擇多個文件,
input的files屬性會返回一個包含所有選中文件的FileList。import React from "react";
const FileDetails: React.FC = () => {
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]; // 獲取第一個文件
if (file) {
console.log(`名稱: ${file.name}`);
console.log(`類 型: ${file.type}`);
console.log(`大小: ${file.size} bytes`);
console.log(`最後修改: ${new Date(file.lastModified)}`);
}
};
return <input type="file" onChange={handleFileChange} />;
};
export default FileDetails; -
拖放操作
當使用拖放功能時,
DataTransfer.files屬性會返回一個FileList。import React from "react";
const DragAndDrop: React.FC = () => {
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); // 阻止預設行為
const files = event.dataTransfer.files; // 獲取 FileList
if (files) {
Array.from(files).forEach((file) => {
console.log(`名稱: ${file.name}`);
console.log(`類型: ${file.type}`);
console.log(`大小: ${file.size} bytes`);
});
}
};
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); // 必須阻止預設行為,否則不會觸發 drop 事件
};
return (
<div
onDrop={handleDrop}
onDragOver={handleDragOver}
style={{
width: "300px",
height: "200px",
border: "2px dashed gray",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
拖放檔案 到這裡
</div>
);
};
export default DragAndDrop;
FileReader:文件資料的解碼工具
FileReader 的角色與用途
在前一章中,我們聊到如何使用 File 和 FileList 來獲取用戶選擇的檔案,但這些檔案實際上還只是「包裹著內容的二進制物件」。如果我們想要讀取這些檔案的內容,比如顯示圖片、解析文字、甚至操作二進制數據,那就需要透過 FileReader。
簡單來說,FileReader 是一個能夠幫助我們「打開檔案內容」的工具,它讓我們能夠非同步地讀取用戶的本地檔案,或者是其他以 Blob 形式存在的資料。
以下是一些 FileReader 的常見應用場景:
- 顯示圖片:使用者上傳一張圖片後,我們可以立即在頁面上預覽。
- 分析文字檔:快速解析檔案中的文字內容,像是讀取
.txt或.json文件。 - 處理二進制資料:用於更進階的應用,例如將文件轉成 Base64 格式(適合上傳小文件)、解析音訊、影片等資料格式。
為什麼是 「非同步」 ?因為讀取文件可能會花時間 (例如上傳一張超大的圖片),而非同步機制可以避免阻塞程式的執行。
FileReader 的三大基石
-
屬性
屬性名稱 描述 error 一個 DOMException物件,用來記錄讀取資料時發生的錯誤資訊。result 讀取到的資料內容,具體的格式取決於使用的讀取方法(例如文字、Base64、二進制等)。 readyState 目前的讀取狀態: 0表示尚未開始讀取(Empty)、1表示正在讀取(Loading)、2表示已完成讀取(Done)。 -
方法
方法名稱 描述 readAsText 將 Blob的內容讀取為文字,完成後會將結果存放在result屬性中。readAsDataURL 將 Blob的內容讀取為 Base64 格式的 Data URL,常用於圖片或文件預覽。readAsBinaryString 將 Blob的內容讀取為原始二進制字串,適合處理更底層的資料操作(較少使用,部分瀏覽器已棄用)。readAsArrayBuffer 將 Blob的內容讀取為ArrayBuffer,適合進行高效的二進制數據處理。abort