ChunkUpload 是一款零依賴、高性能、支持多用戶協作上傳的 Java 斷點續傳組件。採用文件切片 + MD5 秒傳 + 斷點續傳架構,客戶端利用 Web Worker、FileReader、XHR2 等現代 API 實現大文件高效上傳。本文詳細講解服務端集成、客戶端調用、存儲結構、配置調優與示例代碼。
<dependency>
<groupId>com.iyangyuan</groupId>
<artifactId>chunkupload</artifactId>
<version>1.0</version>
</dependency>
<servlet>
<servlet-name>ChunkUpload</servlet-name>
<servlet-class>com.iyangyuan.chunkupload.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ChunkUpload</servlet-name>
<url-pattern>/chunkupload/*</url-pattern>
</servlet-mapping>
建議:放在 Shiro 等安全過濾器之後,Spring MVC 之前,確保權限校驗生效且不被 MVC 攔截。
# 文件存儲根目錄 (多實例部署請指向共享存儲,如 NFS/S3 掛載點)
root=/data/chunkupload
# 文件鎖緩存容量 (根據併發調整,默認 2048)
fileLockCapacity=2048
# 上傳完成是否立即合併切片 (強烈建議 false,異步合併避免長時間阻塞響應)
createFile=false
| 瀏覽器 | 支持 | 備註 |
|---|---|---|
| Chrome 10+ / Firefox 4+ | ✅ 完美 | Web Worker + File API 全支持 |
| Edge 12+ / Safari 10+ | ✅ 完美 | |
| 360/國產雙核 (WebKit 模式) | ✅ 支持 | 需啟用極速模式 |
| IE 所有版本 | ❌ 不支持 | 缺乏 Web Worker/Blob.slice 支持 |
<script src="/static/lib/dawn/dawn.js"></script> <!-- MD5 計算,性能優化 50% -->
<script src="/static/lib/chunkupload/chunkupload.js"></script> <!-- 核心上傳邏輯 -->
// 實例化
var cu = new ChunkUpload(file); // file 為 input.files[0]
// 啟動上傳 (四個異步回調)
cu.upload({
success: function(block) {
// 上傳成功,block 為文件控制對象
console.log("上傳完成", block.info());
},
error: function(msg) {
console.error("上傳失敗:", msg);
},
md5Progress: function(percent) {
console.log("MD5 計算進度:", percent + "%");
},
uploadProgress: function(percent) {
console.log("上傳進度:", percent + "%");
}
});
// 中斷上傳 (任意時刻)
cu.abort();
// 實例化 (需知道文件 MD5 和大小,可從成功回調的 block 獲取,或服務端返回)
var block = new Block("f1154ca6fab7f3628927c1268f3570fd", 20879935);
// 獲取文件信息 (切片列表、狀態、大小)
block.info(function(info) {
console.log(JSON.stringify(info));
/* 輸出示例:
{
"status": 0,
"data": {
"chunks": [{"md5":"e114c2...", "n":1}, ...],
"chunkNum": 5,
"md5": "f1154ca...",
"state": 1, // 1=完成, 0=未完成
"size": 20879935
}
}
*/
}, function(status, errMsg) {
console.error("查詢失敗:", errMsg);
});
// 刪除服務端文件 (所有切片 + 元數據)
block.delete(function(result) {
console.log("刪除結果:", result);
}, function(status, errMsg) {
console.error("刪除失敗:", errMsg);
});
/data/chunkupload/ # root 配置目錄
└── 07/12/87/ # MD5 前6位,每兩位一級目錄
└── 071287fffa974b878732a7a17858be3620879935/ # 目錄名 = MD5 + 文件大小
├── chunk_1.bin # 切片 1 (4MB)
├── chunk_2.bin
├── chunk_3.bin
├── chunk_4.bin
├── chunk_5.bin # 最後一片可能 < 4MB
├── meta.dat # 元數據: 總大小、切片數、狀態、上傳用戶記錄
└── lock.file # 文件級鎖 (防止併發合併衝突)
優勢:目錄分散避免單目錄文件過多導致文件系統性能下降;MD5+大小雙重校驗確保文件唯一性。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ChunkUpload Demo</title>
<style>
body {font-family: sans-serif; max-width: 600px; margin: 2rem auto;}
.progress-bar {width: 100%; height: 20px; background: #eee; border-radius: 10px; overflow: hidden; margin: 10px 0;}
.progress-fill {height: 100%; background: #4caf50; width: 0%; transition: width 0.3s;}
button {padding: 8px 16px; margin-right: 10px; cursor: pointer;}
button:disabled {opacity: 0.5; cursor: not-allowed;}
</style>
</head>
<body>
<h3>大文件斷點續傳演示</h3>
<input type="file" id="fileInput" />
<br/><br/>
<button id="startBtn" onclick="startUpload()">開始上傳</button>
<button id="pauseBtn" onclick="pauseUpload()" disabled>暫停/取消</button>
<div class="progress-bar"><div id="progressFill" class="progress-fill"></div></div>
<p id="statusText">等待選擇文件...</p>
<script src="/static/lib/dawn/dawn.js"></script>
<script src="/static/lib/chunkupload/chunkupload.js"></script>
<script>
var cu, fileInput = document.getElementById('fileInput'),
startBtn = document.getElementById('startBtn'),
pauseBtn = document.getElementById('pauseBtn'),
progressFill = document.getElementById('progressFill'),
statusText = document.getElementById('statusText');
function startUpload() {
var file = fileInput.files[0];
if (!file) return alert("請先選擇文件");
startBtn.disabled = true; pauseBtn.disabled = false;
cu = new ChunkUpload(file);
cu.upload({
md5Progress: function(p) { updateUI(p, "計算 MD5"); },
uploadProgress: function(p) { updateUI(p, "上傳中"); },
success: function(block) {
updateUI(100, "上傳完成 ✅");
startBtn.disabled = false; pauseBtn.disabled = true;
console.log("文件信息:", block.info());
},
error: function(msg) {
statusText.textContent = "失敗: " + msg;
startBtn.disabled = false; pauseBtn.disabled = true;
}
});
}
function pauseUpload() {
if (cu) { cu.abort(); statusText.textContent = "已暫停/取消"; }
startBtn.disabled = false; pauseBtn.disabled = true;
}
function updateUI(percent, label) {
progressFill.style.width = percent + "%";
statusText.textContent = label + " " + percent + "%";
}
</script>
</body>
</html>
createFile=false,啟動定時任務掃描 state=1 且未合併的目錄執行合併,避免上傳請求長時間持有連接。root 指向 NFS/CephFS/MinIO 掛載點,確保多實例訪問同一物理文件。state=0 且超過 7 天未活躍的臨時目錄,釋放磁盤空間。ChunkUpload 以現代瀏覽器能力為基線,捨棄 IE 兼容性換取極致上傳體驗。對於內部管理系統、現代 Web 應用、大文件上傳場景,它是比 Plupload/WebUploader 更輕量、更原生、更易擴展的選擇。