傳統 <input type="file"> 提交表單無法獲取進度。現代方案兩大類:
xhr.upload.onprogress 事件,無需外掛,IE10+ 全支援。<!-- HTML -->
<input type="file" id="fileInput" multiple>
<button onclick="uploadFiles()">上傳</button>
<div id="progressContainer"></div>
<script>
function uploadFiles() {
const files = document.getElementById('fileInput').files;
if (!files.length) return;
const formData = new FormData();
for (let f of files) formData.append('files[]', f);
const xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.php', true);
// 進度條 UI
const container = document.getElementById('progressContainer');
container.innerHTML = '';
const bars = [];
for (let i=0; i<files.length; i++) {
const wrapper = document.createElement('div');
wrapper.innerHTML = `${files[i].name} 0%`;
container.appendChild(wrapper);
bars.push(wrapper.querySelector('progress'));
}
xhr.upload.onprogress = e => {
if (e.lengthComputable) {
const pct = Math.round(e.loaded / e.total * 100);
// 簡單平均顯示總進度,或依檔案索引分配
bars.forEach((bar, idx) => {
bar.value = pct;
bar.nextElementSibling.textContent = pct + '%';
});
}
};
xhr.onload = () => {
if (xhr.status === 200) alert('上傳成功:' + xhr.responseText);
else alert('上傳失敗');
};
xhr.send(formData);
}
</script>
<?php
// upload.php
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
$resp = [];
foreach ($_FILES['files']['tmp_name'] as $idx => $tmp) {
$name = basename($_FILES['files']['name'][$idx]);
$dest = $uploadDir . uniqid() . '_' . $name;
if (move_uploaded_file($tmp, $dest)) {
$resp[] = ['name' => $name, 'path' => $dest, 'status' => 'ok'];
} else {
$resp[] = ['name' => $name, 'status' => 'error'];
}
}
header('Content-Type: application/json');
echo json_encode($resp);
?>
| 庫 | 特點 | 支援 |
|---|---|---|
| blueimp/jQuery-File-Upload | 功能最全:分塊上傳、斷點續傳、圖片縮圖、拖曳、多語言 | jQuery, PHP/Node/Java/Go 後端範例 |
| Uppy | 模組化、React/Vue/Svelte 友善、支援 Tus 協議 (斷點續傳標準) | 現代前端框架 |
| DropzoneJS | 輕量、聲明式、預覽、進度、刪除、並發控制 | 原生 JS / jQuery |
| Tus.io + 任意前端 | 開放協議、斷點續傳標準、支援 S3/本地存儲 | 跨語言、雲原生 |
upload_max_filesize, post_max_size, max_execution_time, max_input_time 需配合大檔調大。client_max_body_size (Nginx) / LimitRequestBody (Apache)。session_write_close() 或改用 Redis Session。原生 XHR2 寫進度條不到 50 行,適合輕量需求;專案若涉及大檔、斷點續傳、多框架整合,直接上 Uppy + Tus 或 blueimp,站在巨人肩膀上避坑。