學會這款 🔥全新設計的 Java 腳手架 ,從此面試不再怕!
在現代 Web 應用中,文件上傳和下載是非常常見的需求。然而,當文件較大時,傳統的上傳下載方式可能會遇到網絡不穩定或傳輸中斷的問題。為了解決這些問題,斷點傳輸(Resumable Upload/Download)成為了一個重要的技術手段。Spring Boot 3 提供了更強大的支持,使得實現斷點傳輸變得更加簡單。
本文將詳細介紹如何在 Spring Boot 3 中實現支持斷點傳輸的文件上傳和下載功能,并通過代碼示例幫助你快速上手。
1. 什么是斷點傳輸?
斷點傳輸是指在文件傳輸過程中,如果傳輸中斷(如網絡故障或用戶手動暫停),可以從斷點處繼續傳輸,而不需要重新開始。這種技術對于大文件傳輸尤為重要,因為它可以顯著減少重復傳輸的時間和帶寬消耗。
- 斷點上傳:客戶端可以將文件分成多個塊(Chunk),分批次上傳,服務器端根據上傳的塊信息進行合并。
- 斷點下載:客戶端可以請求文件的某一部分,服務器端根據請求的范圍返回對應的文件內容。
2. 環境準備
在開始之前,確保你已經具備以下環境:
- JDK 17 或更高版本(Spring Boot 3 要求的最低 JDK 版本)
- Maven 或 Gradle 構建工具
- Spring Boot 3.x
在 pom.xml
中添加以下依賴:
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>
</dependencies>
3. 實現斷點上傳
3.1 配置文件上傳限制
在 application.properties
中配置文件上傳的限制:
# 設置單個文件的最大大小
spring.servlet.multipart.max-file-size=1GB# 設置總上傳文件的最大大小
spring.servlet.multipart.max-request-size=1GB
3.2 實現斷點上傳接口
為了實現斷點上傳,我們需要記錄每個文件的上傳進度。以下是一個簡單的實現:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;@Controller
public class ResumableUploadController {// 保存文件塊的臨時目錄private static final String UPLOAD_DIR = "uploads/";// 用于記錄文件上傳進度的緩存private final Map<String, Long> uploadProgressMap = new HashMap<>();@PostMapping("/upload")@ResponseBodypublic String handleChunkUpload(@RequestParam("file") MultipartFile file,@RequestParam("chunkNumber") int chunkNumber,@RequestParam("totalChunks") int totalChunks,@RequestParam("identifier") String identifier) throws IOException {// 獲取文件塊的大小long chunkSize = file.getSize();String fileName = file.getOriginalFilename();// 創建臨時文件目錄File uploadDir = new File(UPLOAD_DIR);if (!uploadDir.exists()) {uploadDir.mkdirs();}// 將文件塊寫入臨時文件String tempFileName = UPLOAD_DIR + identifier + "_" + fileName;try (RandomAccessFile randomAccessFile = new RandomAccessFile(tempFileName, "rw")) {randomAccessFile.seek((chunkNumber - 1) * chunkSize);randomAccessFile.write(file.getBytes());}// 更新上傳進度uploadProgressMap.put(identifier, (long) chunkNumber);// 如果所有塊都已上傳,合并文件if (chunkNumber == totalChunks) {mergeFileChunks(tempFileName, fileName);uploadProgressMap.remove(identifier);return "Upload complete!";}return "Chunk " + chunkNumber + " uploaded!";}private void mergeFileChunks(String tempFileName, String fileName) throws IOException {File mergedFile = new File(UPLOAD_DIR + fileName);try (RandomAccessFile randomAccessFile = new RandomAccessFile(mergedFile, "rw")) {for (int i = 1; i <= uploadProgressMap.size(); i++) {File chunkFile = new File(tempFileName + "_" + i);byte[] chunkData = Files.readAllBytes(chunkFile.toPath());randomAccessFile.write(chunkData);chunkFile.delete(); // 刪除臨時文件塊}}}
}
3.3 前端實現斷點上傳
在前端,可以使用 JavaScript 將文件分塊并發送到服務器。以下是一個簡單的示例:
<input type="file" id="fileInput" />
<button onclick="uploadFile()">上傳</button><script>async function uploadFile() {const file = document.getElementById('fileInput').files[0];const chunkSize = 1024 * 1024; // 1MBconst totalChunks = Math.ceil(file.size / chunkSize);const identifier = UUID.randomUUID();for (let i = 1; i <= totalChunks; i++) {const chunk = file.slice((i - 1) * chunkSize, i * chunkSize);const formData = new FormData();formData.append('file', chunk);formData.append('chunkNumber', i);formData.append('totalChunks', totalChunks);formData.append('identifier', identifier);await fetch('/upload', {method: 'POST',body: formData});}alert('文件上傳完成!');}
</script>
4. 實現斷點下載
4.1 實現斷點下載接口
Spring Boot 3 支持通過 Range
請求頭實現斷點下載。以下是一個簡單的實現:
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRange;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;@RestController
public class ResumableDownloadController {private static final String DOWNLOAD_DIR = "uploads/";@GetMapping("/download/{filename:.+}")public ResponseEntity<Resource> downloadFile(@PathVariable String filename,@RequestHeader(value = "Range", required = false) String rangeHeader) throws IOException {Path path = Paths.get(DOWNLOAD_DIR + filename);Resource resource = new UrlResource(path.toUri());if (!resource.exists() || !resource.isReadable()) {return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);}long fileLength = resource.contentLength();if (rangeHeader == null) {// 如果沒有 Range 請求頭,返回整個文件return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"").header(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileLength)).body(resource);} else {// 解析 Range 請求頭List<HttpRange> ranges = HttpRange.parseRanges(rangeHeader);HttpRange range = ranges.get(0);long start = range.getRangeStart(fileLength);long end = range.getRangeEnd(fileLength);// 返回部分文件內容long rangeLength = end - start + 1;return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"").header(HttpHeaders.CONTENT_RANGE, "bytes " + start + "-" + end + "/" + fileLength).header(HttpHeaders.CONTENT_LENGTH, String.valueOf(rangeLength)).body(resource);}}
}
4.2 測試斷點下載
使用支持斷點下載的工具(如 curl
或瀏覽器)測試下載功能。例如:
curl -H "Range: bytes=0-1023" http://localhost:8080/download/example.txt -o example.txt
5. 總結
通過本文的介紹,你已經學會了如何在 Spring Boot 3 中實現支持斷點傳輸的文件上傳和下載功能。斷點傳輸技術可以顯著提升大文件傳輸的效率和可靠性,是現代 Web 應用中不可或缺的一部分。
希望本文的內容能夠幫助你在實際項目中更好地處理文件上傳和下載的需求。如果你有任何問題或建議,歡迎在評論區留言討論。Happy coding!