在 Spring Boot 应用中实现文件上传功能,包括秒传、断点续传和分片上传,可以显著提升用户体验和系统性能。下面是一个详细的实现方案,包括代码示例和关键步骤。
1. 项目依赖
首先,在 pom.xml 文件中添加必要的依赖:
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Thymeleaf (可选) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Lombok (可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Commons IO -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>
</dependencies>
2. 配置文件
在 application.yml 或 application.properties 中配置文件上传的相关参数:
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
3. 文件上传控制器
创建一个控制器来处理文件上传请求。
3.1 秒传
秒传的核心思想是通过计算文件的哈希值(如 MD5)来判断文件是否已经存在。
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@RestController
@RequestMapping("/upload")
public class FileUploadController {
@PostMapping("/fast")
public String fastUpload(@RequestParam("file") MultipartFile file) throws IOException, NoSuchAlgorithmException {
String md5 = calculateMD5(file.getInputStream());
File destFile = new File("upload/" + md5);
if (destFile.exists()) {
return "文件已存在,秒传成功!";
}
file.transferTo(destFile);
return "文件上传成功!";
}
private String calculateMD5(InputStream inputStream) throws NoSuchAlgorithmException, IOException {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
md.update(buffer, 0, bytesRead);
}
byte[] digest = md.digest();
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
3.2 断点续传
断点续传的核心思想是通过 HTTP 范围请求来实现部分文件的上传。
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@RestController
@RequestMapping("/upload")
public class FileUploadController {
@PostMapping("/resume")
public ResponseEntity<String> resumeUpload(@RequestParam("file") MultipartFile file, @RequestParam("start") long start) throws IOException {
File destFile = new File("upload/" + file.getOriginalFilename());
if (!destFile.exists()) {
destFile.createNewFile();
}
try (FileOutputStream fos = new FileOutputStream(destFile, true)) {
byte[] bytes = file.getBytes();
fos.write(bytes, 0, (int) (bytes.length - start));
}
HttpHeaders headers = new HttpHeaders();
headers.add("Range", "bytes=" + start + "-" + (start + file.getSize() - 1));
return new ResponseEntity<>("文件上传成功!", headers, HttpStatus.PARTIAL_CONTENT);
}
}
3.3 分片上传
分片上传的核心思想是将大文件拆分成多个小文件分别上传,最后合并成一个完整的文件。
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@RestController
@RequestMapping("/upload")
public class FileUploadController {
@PostMapping("/chunk")
public String chunkUpload(@RequestParam("file") MultipartFile file, @RequestParam("index") int index, @RequestParam("totalChunks") int totalChunks) throws IOException {
File chunkDir = new File("upload/chunks");
if (!chunkDir.exists()) {
chunkDir.mkdirs();
}
File chunkFile = new File(chunkDir, index + "_" + file.getOriginalFilename());
file.transferTo(chunkFile);
if (index == totalChunks - 1) {
mergeChunks(file.getOriginalFilename(), totalChunks);
}
return "分片上传成功!";
}
private void mergeChunks(String filename, int totalChunks) throws IOException {
File destFile = new File("upload/" + filename);
if (destFile.exists()) {
destFile.delete();
}
try (FileOutputStream fos = new FileOutputStream(destFile)) {
for (int i = 0; i < totalChunks; i++) {
File chunkFile = new File("upload/chunks/" + i + "_" + filename);
try (FileInputStream fis = new FileInputStream(chunkFile)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
chunkFile.delete();
}
}
}
}
4. 前端页面
创建一个简单的 HTML 页面来测试文件上传功能。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<h1>秒传</h1>
<form action="/upload/fast" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button type="submit">上传</button>
</form>
<h1>断点续传</h1>
<form action="/upload/resume" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="number" name="start" value="0">
<button type="submit">上传</button>
</form>
<h1>分片上传</h1>
<form action="/upload/chunk" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="number" name="index" value="0">
<input type="number" name="totalChunks" value="1">
<button type="submit">上传</button>
</form>
</body>
</html>
5. 运行项目
启动 Spring Boot 项目,并访问前端页面进行文件上传测试。
总结
通过上述步骤,你可以在 Spring Boot 项目中实现文件上传的秒传、断点续传和分片上传功能。这些功能可以显著提升文件上传的效率和用户体验。