檔案上傳系統設計 (6) —— 前端上傳的專業修養
在前面的篇幅中,我們把後端的「內功」練好了。現在,我們要回到使用者最直接接觸的地方:瀏覽器前端。
一個專業的檔案上傳功能,絕不是放個按鈕、選完檔案就轉圈圈等待而已。使用者需要知道「傳了多少」、需要有「後悔取消」的權利,甚至希望不用打開資料夾,直接從桌面「拖進來」或「貼進來」就能完成。
一、 進度監控:讓使用者等得心安
當檔案超過 2MB 時,如果沒有進度條,使用者會陷入「這是有在傳,還是卡住了?」的焦慮。
1. 傳統但強大的 XMLHttpRequest (XHR)
雖然現在流行 fetch,但在監控 上傳 (Upload) 進度方面,XHR 仍然是最直覺的選擇。
javascript
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append("file", file);
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
console.log(`上傳進度:${percent}%`);
// 更新 UI 進度條
}
};
xhr.open("POST", "/upload");
xhr.send(formData);2. 為何 Fetch 比較難監控「上傳」進度?
現代的 fetch API 原生支援監控 下載 進度(透過 ReadableStream),但對於 上傳 進度的支持目前在各瀏覽器中仍不夠純熟。這也是為什麼許多專業上傳套件(如 axios 或 Uppy)在底層仍會選用 XHR。
二、 撤回權:如何取消正在進行的上傳?
假設使用者點錯了檔案,或突然網路變慢不想傳了,你的 UI 必須提供一個「取消」按鈕。
使用 AbortController
這是現代 Javascript 的標準作法,可以用來中斷任何 fetch 請求。
javascript
const controller = new AbortController();
const signal = controller.signal;
fetch("/upload", {
method: "POST",
body: formData,
signal: signal, // 綁定信號
}).catch((err) => {
if (err.name === "AbortError") {
console.log("上傳已被使用者取消");
}
});
// 當點擊取消按鈕時執行:
// controller.abort();三、 進階交互:拖放與貼上 (Drag, Drop & Paste)
專業的產品(如 GitHub, Slack)都支持這兩種操作:
1. 拖放 (Drag & Drop)
使用者不需要點擊 Input,直接將檔案拖入特定目標區域。
javascript
const dropZone = document.getElementById("drop-zone");
dropZone.ondragover = (e) => e.preventDefault(); // 必須阻擋預設行為
dropZone.ondrop = (e) => {
e.preventDefault();
const files = e.dataTransfer.files; // 這裡拿到檔案列表
handleUpload(files[0]);
};2. 剪貼簿貼上 (Paste)
當你截圖後(或在資料夾複製檔案),直接在網頁 Ctrl+V (或 Cmd+V)。這對聊天室或 Markdown 編輯器極其好用。
javascript
document.addEventListener("paste", (e) => {
const items = e.clipboardData.items;
for (let item of items) {
if (item.kind === "file") {
const file = item.getAsFile();
handleUpload(file);
}
}
});總結
前端的專業度體現在「掌控感」與「便捷度」:
- 進度條:利用
xhr.upload即時回報,消弭等待焦慮。 - 取消機制:使用
AbortController賦予使用者反悔的權力。 - 多元輸入:透過
Drag & Drop與Paste大幅提升操作效率。
下一篇,我們將聊聊如何透過「客戶端預處理」進一步減少伺服器壓力。
️ 進階挑戰
在顯示進度條時,為什麼我們通常不建議直接顯示到 100%?當進度條跑到 100% 但後端還沒回傳 Response 時,你會如何優化 UI 提示?