SpringBoot :文件上传之秒传、断点续传、分片上传

在 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 项目中实现文件上传的秒传、断点续传和分片上传功能。这些功能可以显著提升文件上传的效率和用户体验。

原文链接:,转发请注明来源!