技術選型理由
- Baidu WebUploader:HTML5 主流 + Flash 降級,支持 IE6+、iOS 6+、Android 4+
- 核心優勢:大文件分片並發上傳、斷點續傳、拖拽/截圖粘貼、縮略圖預覽
- 僅提供底層邏輯,UI 完全可定制
前端核心配置
HTML 結構
<div id="uploader" class="wu-example">
<div class="queueList">
<div id="dndArea" class="placeholder">
<div id="filePicker"></div>
<p>或將照片拖到這裡,單次最多可選 300 張</p>
</div>
</div>
<div class="statusBar">
<div class="progress"><span class="text">0%</span></div>
<div class="info"></div>
<div class="btns">
<div id="filePicker2"></div>
<div class="uploadBtn">開始上傳</div>
</div>
</div>
</div>
JavaScript 初始化關鍵參數
var uploader = WebUploader.create({
// 選文件按鈕
pick: { id: '#filePicker', label: '點擊選擇圖片' },
// 拖拽區域
dnd: '#uploader .queueList',
// 截圖粘貼
paste: document.body,
// 文件類型過濾
accept: {
title: 'Images',
extensions: 'gif,jpg,jpeg,bmp,png',
mimeTypes: 'image/*'
},
// Flash 降級路徑
swf: 'js/plugins/webuploader/Uploader.swf',
disableGlobalDnd: true,
// ★ 分片上傳核心
chunked: true,
chunkSize: 5 * 1024 * 1024, // 5MB/片
threads: 3, // 並發分片數
// 後端接收地址
server: 'https://localhost:8080/test/upload.do?method=upload',
// 限制
fileNumLimit: 300,
fileSizeLimit: 200 * 1024 * 1024, // 200MB 總大小
fileSingleSizeLimit: 50 * 1024 * 1024 // 50MB 單文件
});
狀態管理與 UI 交互(精簡版)
// 文件加入隊列
uploader.onFileQueued = function(file) {
fileCount++; fileSize += file.size;
renderFileItem(file); // 創建 li、生成縮略圖、綁定刪除/旋轉
updateStatus('ready');
};
// 上傳進度
uploader.onUploadProgress = function(file, percentage) {
$('#'+file.id).find('.progress span').css('width', percentage*100+'%');
updateTotalProgress();
};
// 狀態機:pedding → ready → uploading → paused → confirm → finish
function setState(val) {
// 切換按鈕文本、顯隱進度條、刷新 uploader
}
後端 Spring MVC 實現
分片接收與合並控制器
@Controller
@RequestMapping("/upload")
public class UploadController {
private static final String UPLOAD_ROOT = "/data/upload/";
private static final String TEMP_DIR = UPLOAD_ROOT + "temp/";
private static final String FINAL_DIR = UPLOAD_ROOT + "files/";
@RequestMapping(params = "method=upload")
@ResponseBody
public String upload(@RequestParam("file") MultipartFile chunk,
@RequestParam("chunk") Integer chunkIndex,
@RequestParam("chunks") Integer totalChunks,
@RequestParam("name") String fileName,
@RequestParam("md5") String fileMd5) {
// 1. 分片臨時存儲:temp/{md5}/{chunk}
File tempFile = new File(TEMP_DIR + fileMd5 + "/" + chunkIndex);
tempFile.getParentFile().mkdirs();
try { chunk.transferTo(tempFile); }
catch (IOException e) { return jsonError("分片存儲失敗"); }
// 2. 最後一片 → 合並
if (chunkIndex == totalChunks - 1) {
File finalFile = new File(FINAL_DIR + fileName);
try (FileOutputStream fos = new FileOutputStream(finalFile)) {
for (int i = 0; i < totalChunks; i++) {
File part = new File(TEMP_DIR + fileMd5 + "/" + i);
Files.copy(part.toPath(), fos);
part.delete(); // 清理分片
}
new File(TEMP_DIR + fileMd5).delete(); // 清理目錄
} catch (IOException e) { return jsonError("合並失敗"); }
// 3. 入庫記錄(路徑、MD5、大小、類型...)
// fileService.save(...);
}
return jsonSuccess();
}
private String jsonSuccess() { return "{\"msg\":\"success\"}"; }
private String jsonError(String msg) { return "{\"msg\":\"error\",\"error\":\""+msg+"\"}"; }
}
Spring MVC 文件上傳配置
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="52428800"/> <!-- 50MB 單片上限 -->
<property name="maxInMemorySize" value="4096"/>
</bean>
斷點續傳增強(可選)
- 上傳前調用
uploader.md5File(file) 計算文件 MD5(WebUploader 內置 SparkMD5)
- 前端發送
md5 參數,後端檢查 temp/{md5}/ 目錄下已存在分片
- 跳過已上傳分片,僅上傳缺失部分
- 秒傳邏輯:最終目錄存在同 MD5 文件 → 直接返回成功
生產部署注意事項
- Nginx 反代:設置
client_max_body_size 50M; proxy_read_timeout 300s;
- 臨時目錄清理:定時任務清理 24h 前未完成的
temp/ 目錄
- 文件名安全:存儲用 UUID/MD5 命名,原始名存數據庫,防路徑遍歷
- 權限控制:上傳接口需認證,限制用戶配額
- 集群共享存儲:多實例需掛載 NFS/NAS 或對象存儲(S3/OSS)