Java實(shí)現(xiàn)大文件上傳與斷點(diǎn)續(xù)傳的全過程
在現(xiàn)代 Web 應(yīng)用中,用戶經(jīng)常需要上傳大型文件——如高清視頻、設(shè)計(jì)圖紙或數(shù)據(jù)庫備份。然而,受限于網(wǎng)絡(luò)穩(wěn)定性、服務(wù)器內(nèi)存和瀏覽器限制,直接上傳大文件極易失敗。為此,“分片上傳 + 斷點(diǎn)續(xù)傳”成為業(yè)界標(biāo)準(zhǔn)解決方案。本文將深入講解如何使用 Java(基于 Spring Boot) 實(shí)現(xiàn)高效、可靠的大文件上傳系統(tǒng)。
一、為什么需要斷點(diǎn)續(xù)傳?
- 網(wǎng)絡(luò)不穩(wěn)定:上傳過程中斷后,無需從頭開始。
- 節(jié)省帶寬與時(shí)間:僅重傳失敗或未傳的分片。
- 提升用戶體驗(yàn):支持上傳進(jìn)度顯示、暫停/恢復(fù)。
- 避免服務(wù)器壓力:避免一次性加載整個(gè)大文件到內(nèi)存。
二、核心設(shè)計(jì)思想
1. 文件分片(Chunking)
將一個(gè)大文件按固定大小(如 2MB)切分為多個(gè)小塊(chunks),每個(gè)塊獨(dú)立上傳。
2. 唯一標(biāo)識
使用文件內(nèi)容的 MD5 值 作為唯一 ID,既可識別文件,又能避免重復(fù)上傳相同內(nèi)容。
3. 分片管理
- 后端為每個(gè)文件創(chuàng)建臨時(shí)目錄,按序號存儲分片。
- 提供接口查詢“已上傳分片列表”,實(shí)現(xiàn)斷點(diǎn)檢測。
4. 合并與清理
所有分片收齊后,按順序合并成完整文件,并清理臨時(shí)數(shù)據(jù)。
三、后端實(shí)現(xiàn)(Spring Boot)
1. 項(xiàng)目依賴(Maven)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
2. 控制器代碼
@RestController
@RequestMapping("/api/upload")
public class ResumableUploadController {
private static final String TEMP_DIR = "uploads/temp/";
private static final String FINAL_DIR = "uploads/final/";
/**
* 檢查哪些分片已上傳(用于斷點(diǎn)續(xù)傳)
*/
@GetMapping("/check")
public Set<Integer> checkUploadedChunks(@RequestParam String fileMd5) {
Set<Integer> uploaded = new HashSet<>();
Path tempPath = Paths.get(TEMP_DIR, fileMd5);
if (Files.exists(tempPath)) {
try (Stream<Path> stream = Files.list(tempPath)) {
stream.forEach(p -> {
String name = p.getFileName().toString();
if (name.matches("\\d+")) {
uploaded.add(Integer.parseInt(name));
}
});
} catch (IOException e) {
// 日志記錄
}
}
return uploaded;
}
/**
* 上傳單個(gè)分片
*/
@PostMapping("/chunk")
public ResponseEntity<?> uploadChunk(
@RequestParam String fileMd5,
@RequestParam int chunkIndex,
@RequestParam MultipartFile chunk) {
try {
Path tempDir = Paths.get(TEMP_DIR, fileMd5);
Files.createDirectories(tempDir);
Path chunkFile = tempDir.resolve(String.valueOf(chunkIndex));
chunk.transferTo(chunkFile.toFile());
return ResponseEntity.ok().build();
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
/**
* 合并所有分片為完整文件
*/
@PostMapping("/merge")
public ResponseEntity<?> mergeChunks(
@RequestParam String fileMd5,
@RequestParam String fileName,
@RequestParam int totalChunks) {
try {
Path finalPath = Paths.get(FINAL_DIR, fileName);
Files.createDirectories(finalPath.getParent());
try (OutputStream out = Files.newOutputStream(finalPath)) {
for (int i = 0; i < totalChunks; i++) {
Path chunk = Paths.get(TEMP_DIR, fileMd5, String.valueOf(i));
if (!Files.exists(chunk)) {
return ResponseEntity.badRequest()
.body("Missing chunk: " + i);
}
byte[] data = Files.readAllBytes(chunk);
out.write(data);
}
}
// 清理臨時(shí)分片
FileUtils.deleteDirectory(new File(TEMP_DIR + fileMd5));
return ResponseEntity.ok(Map.of("success", true, "path", finalPath.toString()));
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
注意:實(shí)際項(xiàng)目中應(yīng)加入文件名校驗(yàn)、權(quán)限控制、異常日志等。
四、前端配合邏輯(簡要)
前端需完成以下步驟:
- 計(jì)算文件 MD5(推薦使用
spark-md5庫)。 - 調(diào)用
/check接口 獲取已上傳分片索引。 - 循環(huán)上傳未完成的分片,每個(gè)請求攜帶:
fileMd5chunkIndex- 分片 Blob 數(shù)據(jù)
- 全部上傳完成后,調(diào)用
/merge觸發(fā)合并。
// 示例:上傳一個(gè)分片
const uploadChunk = async (fileMd5, index, blob) => {
const formData = new FormData();
formData.append('fileMd5', fileMd5);
formData.append('chunkIndex', index);
formData.append('chunk', blob);
await fetch('/api/upload/chunk', { method: 'POST', body: formData });
};
五、關(guān)鍵優(yōu)化建議
| 優(yōu)化點(diǎn) | 說明 |
|---|---|
| MD5 計(jì)算 | 前端計(jì)算可避免重復(fù)上傳;若安全要求高,后端也可二次校驗(yàn)。 |
| 并發(fā)上傳 | 允許同時(shí)上傳多個(gè)分片(如 3~5 個(gè)),提升速度。 |
| 超時(shí)清理 | 定時(shí)任務(wù)刪除 24 小時(shí)未合并的臨時(shí)分片,防止磁盤占滿。 |
| 限流與鑒權(quán) | 防止惡意上傳,限制單用戶上傳頻率和總大小。 |
| 進(jìn)度反饋 | 前端根據(jù)已傳分片數(shù)實(shí)時(shí)更新進(jìn)度條。 |
六、進(jìn)階方案
1. 使用 TUS 協(xié)議
TUS 是一個(gè)開源的 Resumable Upload 標(biāo)準(zhǔn)。Java 社區(qū)有 tus-java-server 實(shí)現(xiàn),開箱即用,支持跨平臺、跨語言。
2. 對接云存儲
阿里云 OSS、騰訊云 COS、AWS S3 等均提供 分片上傳 API??蓪⒎制苯由蟼髦猎拼鎯?,后端僅負(fù)責(zé)協(xié)調(diào),大幅降低服務(wù)器負(fù)載。
七、總結(jié)
通過“分片上傳 + 斷點(diǎn)續(xù)傳”,我們不僅能可靠地處理 GB 級文件,還能顯著提升用戶體驗(yàn)和系統(tǒng)健壯性。雖然實(shí)現(xiàn)細(xì)節(jié)較多,但核心邏輯清晰:切分 → 上傳 → 校驗(yàn) → 合并。
在實(shí)際項(xiàng)目中,建議結(jié)合業(yè)務(wù)需求選擇自研或集成成熟方案。對于高并發(fā)、高可靠場景,優(yōu)先考慮云廠商的分片上傳能力;對于內(nèi)部系統(tǒng)或定制化需求,本文提供的 Java 實(shí)現(xiàn)可作為堅(jiān)實(shí)基礎(chǔ)。
以上就是Java實(shí)現(xiàn)大文件上傳與斷點(diǎn)續(xù)傳的全過程的詳細(xì)內(nèi)容,更多關(guān)于Java大文件上傳與斷點(diǎn)續(xù)傳的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java編程中應(yīng)用的GUI設(shè)計(jì)基礎(chǔ)
這篇文章主要介紹了Java編程中應(yīng)用的GUI設(shè)計(jì)基礎(chǔ),為一些Java開發(fā)CS類型應(yīng)用的基礎(chǔ)概念知識,需要的朋友可以參考下2015-10-10
SpringBoot如何讀取mock數(shù)據(jù)(高效調(diào)試接口)
本文介紹如何在SpringBoot項(xiàng)目中讀取resources目錄下的mock數(shù)據(jù)文件,以便高效調(diào)試接口,在 Spring Boot 項(xiàng)目中,通常會將靜態(tài)資源或配置文件放在 src/main/resources 目錄下,下面通過實(shí)例給大家詳細(xì)介紹,感興趣的朋友一起看看吧2024-12-12
java創(chuàng)建xls文件放到指定文件夾中實(shí)現(xiàn)方式
本文介紹了如何在Java中使用ApachePOI庫創(chuàng)建和操作Excel文件,重點(diǎn)是如何創(chuàng)建一個(gè)XLS文件并將其放置到指定文件夾中2025-11-11
java_時(shí)間戳與Date_相互轉(zhuǎn)化的實(shí)現(xiàn)代碼
本篇文章是對java_時(shí)間戳與Date_相互轉(zhuǎn)化的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下如下。2016-11-11
java 中InputStream,String,File之間的相互轉(zhuǎn)化對比
這篇文章主要介紹了java 中InputStream,String,File之間的相互轉(zhuǎn)化對比的相關(guān)資料,需要的朋友可以參考下2017-04-04

