Spring Boot 3 文件下载、多文件下载以及大文件分片下载、文件流处理、批量操作 和 分片技术

在 Spring Boot 3 中,实现文件下载、多文件下载以及大文件分片下载需要结合以下功能:文件流处理、批量操作 和 分片技术。以下是详细实现方案:

1. 单文件下载

基础的单文件下载实现,可以参考以下代码:

java 复制代码
@GetMapping("/download")
public ResponseEntity<Resource> downloadFile(@RequestParam String filename) {
    try {
        Path filePath = Paths.get("/path/to/files").resolve(filename).normalize();
        Resource resource = new UrlResource(filePath.toUri());

        if (!resource.exists()) {
            return ResponseEntity.notFound().build();
        }

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(resource);
    } catch (MalformedURLException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
}

2. 多文件打包下载

对于多文件下载,可以通过将文件打包为 ZIP 文件,再进行下载。

控制器代码

java 复制代码
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@RestController
public class MultiFileDownloadController {

    private final String FILE_DIRECTORY = "/path/to/files";

    @GetMapping("/download-multiple")
    public ResponseEntity<byte[]> downloadMultipleFiles(@RequestParam List<String> filenames) {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ZipOutputStream zos = new ZipOutputStream(baos)) {

            for (String filename : filenames) {
                Path filePath = Paths.get(FILE_DIRECTORY).resolve(filename).normalize();
                if (Files.exists(filePath)) {
                    zos.putNextEntry(new ZipEntry(filename));
                    Files.copy(filePath, zos);
                    zos.closeEntry();
                }
            }

            zos.finish();

            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"files.zip\"");

            return ResponseEntity.ok()
                    .headers(headers)
                    .body(baos.toByteArray());

        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

前端测试请求

使用 fetch 或 axios,发送文件名列表参数,接收 ZIP 文件。

3. 大文件分片下载

对于大文件分片下载,结合 HTTP Range 请求 来实现。

控制器代码

java 复制代码
@GetMapping("/download-chunk")
public ResponseEntity<Resource> downloadFileInChunks(
        @RequestParam String filename,
        @RequestHeader(value = "Range", required = false) String rangeHeader) {
    try {
        Path filePath = Paths.get("/path/to/files").resolve(filename).normalize();
        Resource resource = new UrlResource(filePath.toUri());

        if (!resource.exists()) {
            return ResponseEntity.notFound().build();
        }

        long fileSize = Files.size(filePath);
        long rangeStart = 0, rangeEnd = fileSize - 1;

        if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
            String[] ranges = rangeHeader.replace("bytes=", "").split("-");
            rangeStart = Long.parseLong(ranges[0]);
            if (ranges.length > 1) {
                rangeEnd = Long.parseLong(ranges[1]);
            }
        }

        if (rangeStart > rangeEnd || rangeEnd >= fileSize) {
            return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE).build();
        }

        long chunkSize = rangeEnd - rangeStart + 1;
        InputStream inputStream = Files.newInputStream(filePath);
        inputStream.skip(rangeStart);

        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_RANGE, "bytes " + rangeStart + "-" + rangeEnd + "/" + fileSize);
        headers.add(HttpHeaders.ACCEPT_RANGES, "bytes");

        return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
                .headers(headers)
                .contentLength(chunkSize)
                .body(new InputStreamResource(inputStream));

    } catch (IOException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
}

说明

Range 请求:

客户端通过设置 Range: bytes=start-end 来请求文件的部分内容。

响应头包含 Content-Range 和 Accept-Ranges,支持分片下载。

HTTP 状态码:

206 Partial Content:表示成功返回部分内容。

416 Requested Range Not Satisfiable:请求范围无效。

文件流处理:

使用 InputStream.skip() 跳过已读取部分。

确保响应的 Content-Length 与分片大小匹配。

4. 结合前端

分片下载示例(React)

js 复制代码
function downloadLargeFile(filename) {
    const chunkSize = 1024 * 1024; // 每片1MB
    let start = 0;

    function fetchChunk() {
        fetch(`/download-chunk?filename=${filename}`, {
            headers: {
                Range: `bytes=${start}-${start + chunkSize - 1}`
            }
        })
            .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                return response.blob();
            })
            .then(blob => {
                saveAs(blob, filename); // 使用 FileSaver.js 保存
                start += chunkSize;
                if (blob.size === chunkSize) {
                    fetchChunk(); // 继续下载下一片
                }
            })
            .catch(err => console.error(err));
    }

    fetchChunk();
}

5. 总结

单文件下载:简单场景适用。

多文件下载:打包为 ZIP 文件传输,适合批量文件场景。

大文件分片下载:支持断点续传,提升用户体验,尤其适合视频流或超大文件传输。

根据具体业务需求,选择合适的实现方案。如果需要更多优化,比如 CDN 加速或并行分片,可以在此基础上扩展实现。

相关推荐
奈川直子2 分钟前
EasyExcel自定义动态下拉框(附加业务对象转换功能)
java·mysql·spring·excel
老马啸西风8 分钟前
NLP 中文拼写检测纠正论文-07-NLPTEA-2020中文语法错误诊断共享任务概述
java
好看资源平台43 分钟前
Java Web开发基础——Java Web项目的结构与组织
java
->yjy44 分钟前
[微服务] - MQ高级
java·微服务·架构
TANGLONG2221 小时前
【C++】穿越时光隧道,拾贝史海遗珍,轻启C++入门之钥,解锁程序之奥秘(首卷)
java·c语言·数据结构·c++·redis·python·算法
栗筝i1 小时前
Spring 核心技术解析【纯干货版】- IV:Spring 切面编程模块 Spring-Aop 模块精讲
java·数据库·spring
尘浮生1 小时前
Java项目实战II基于小程序的驾校管理系统(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·mysql·微信小程序·小程序
武昌库里写JAVA1 小时前
Springboot 升级带来的Swagger异常
数据结构·vue.js·spring boot·算法·课程设计
Y编程小白1 小时前
沙箱模拟支付宝支付3--支付的实现
java·沙箱支付
山山而川粤1 小时前
酒店管理系统|Java|SSM|VUE| 前后端分离
java·开发语言·后端·学习·mysql