升级 Spring Boot 3 配置讲解 —— 支持断点传输的文件上传和下载功能

学会这款 🔥全新设计的 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")
    @ResponseBody
    public 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; // 1MB
        const 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!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/950792.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

R语言安装教程与常见问题

生物信息基础入门笔记 R语言安装教程与常见问题 今天和大家聊一个非常基础但是很重要的技术问题——如何在不同操作系统上安装R语言&#xff1f;作为生物信息学数据分析的神兵利器&#xff0c;R语言的安装可谓是入门第一步&#xff0c;学术打工人的必备技能。今天分享在Windows…

UI自动化测试保姆级教程--pytest详解(精简易懂)

欢迎来到啊妮莫的学习小屋 别让过去的悲伤&#xff0c;毁掉当下的快乐一《借东西的小人阿莉埃蒂》 简介 pytest是一个用于Python的测试框架, 支持简单的单元测试和复杂的功能测试. 和Python自带的UnitTest框架类似, 但是相比于UnitTest更加简洁, 效率更高. 特点 非常容易上手…

基类指针指向派生类对象,基类指针的首地址永远指向子类从基类继承的基类首地址

文章目录 基类指针指向派生类对象&#xff0c;基类指针的首地址永远指向子类从基类继承的基类起始地址。代码代码2 基类指针指向派生类对象&#xff0c;基类指针的首地址永远指向子类从基类继承的基类起始地址。 代码 #include <iostream> using namespace std;class b…

分布式Id方案选择

分布式 ID 方案选择 在当今分布式系统日益盛行的背景下&#xff0c;分布式 ID 生成方案的选择成为了众多开发者关注的焦点。一个优秀的分布式 ID 方案&#xff0c;不仅能够确保生成的 ID 全局唯一&#xff0c;避免数据冲突&#xff0c;还能在高并发、大规模的分布式环境中保持…

花生好坏缺陷识别数据集,7262张图片,支持yolo,coco json,pasical voc xml格式的标注,识别准确率在95.7%

花生好坏缺陷识别数据集,7262张图片&#xff0c;支持yolo&#xff0c;coco json&#xff0c;pasical voc xml格式的标注&#xff0c;识别准确率在95.7% 数据集分割 训练组87&#xff05; 6353图片 有效集8% 606图片 测试集4% 303图片 预处理 自动定…

springboot 集成 etcd

springboot 集成 etcd 往期内容 ETCD 简介docker部署ETCD 前言 好久不见各位小伙伴们&#xff0c;上两期内容中&#xff0c;我们对于分布式kv存储中间件有了简单的认识&#xff0c;完成了docker-compose 部署etcd集群以及可视化工具 etcd Keeper&#xff0c;既然有了认识&a…

【递归与分治】Leetcode23:合并K个升序链表

一、题目描述 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 示例 1&#xff1a; 输入&#xff1a;lists [[1,4,5],[1,3,4],[2,6]] 输出&#xff1a;[1,1,2,3,4,4,5,6] 解释&#xff1a;链表数…

将分支A某一个commit合并到分支B

1.寻找A分支的commit 在分支B下&#xff0c;点击git找到分支A的历史提交记录,如图所示&#xff1a; 2.点击分支A的某个commit&#xff0c;进行合并到分支B 将这个commit&#xff0c;进行cherry-Pick&#xff0c;就可以把分支A的合并到分支B上的本地仓库中&#xff0c;然后就可…

如何快速上手一个鸿蒙工程

作为一名鸿蒙程序猿&#xff0c;当你换了一家公司&#xff0c;或者被交接了一个已有的业务。前辈在找你之前十分钟写了一个他都看不懂的交接文档&#xff0c;然后把一个鸿蒙工程交接给你了&#xff0c;说以后就是你负责了。之后几天你的状态大概就是下边这样的&#xff0c;一堆…

预训练语言模型——BERT

1.预训练思想 有了预训练就相当于模型在培养大学生做任务&#xff0c;不然模型初始化再做任务就像培养小学生 当前数据层面的瓶颈是能用于预训练的语料快被用完了 现在有一个重要方向是让机器自己来生成数据并做微调 1.1 预训练&#xff08;Pre - training&#xff09;vs. 传…

关于FPGA(现场可编程门阵列)工程技术人员的详细介绍

一、FPGA工程技术人员概述 FPGA工程技术人员是专注于现场可编程门阵列&#xff08;FPGA&#xff09;设计、开发、测试及优化的专业技术人员。他们利用FPGA的灵活性和可编程性&#xff0c;为各种应用创建高效、定制化的硬件解决方案。 二、主要工作任务 FPGA逻辑设计&#xf…

机器学习模型评估指标

模型的评估指标是衡量一个模型应用于对应任务的契合程度&#xff0c;常见的指标有&#xff1a; 准确率&#xff08;Accuracy&#xff09;: 正确预测的样本数占总样本数的比例。适用于类别分布均衡的数据集。 精确率&#xff08;Precision&#xff09;: 在所有被预测为正类的样…

基于html5实现音乐录音播放动画源码

源码介绍 基于html5实现音乐录音播放动画源码是一款类似Shazam的UI&#xff0c;点击按钮后&#xff0c;会变成为一个监听按钮。旁边会有音符飞入这个监听按钮&#xff0c;最后转换成一个音乐播放器。 效果预览 源码获取 基于html5实现音乐录音播放动画源码

基于深度学习的视觉检测小项目(六) 项目的信号和变量的规划

• 关于前后端分离 当前流行的一种常见的前后端分离模式是vueflask&#xff0c;vueflask模式的前端和后端之间进行数据的传递通常是借助 API&#xff08;应用程序编程接口&#xff09;来完成的。vue通过调用后端提供的 API 来获取或提交数据。例如&#xff0c;前端可能通过发送…

文件传输速查表:Windows 和 Linux

文件传输速查表&#xff1a;Windows 和 Linux 免责申明 本文章仅供网络安全相关学习与研究使用&#xff0c;旨在促进技术交流与安全知识普及&#xff0c;严禁将本文内容及相关工具用于未授权的渗透测试或任何违法活动。 重要声明&#xff1a; 由于传播、使用本文章所提供的信…

基于SpringBoot+Vue的“有光”摄影分享网站系统

基于SpringBootVue的“有光”摄影分享网站系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345…

课题推荐——基于GPS的无人机自主着陆系统设计

关于“基于GPS的无人机自主着陆系统设计”的详细展开&#xff0c;包括项目背景、具体内容、实施步骤和创新点。如需帮助&#xff0c;或有导航、定位滤波相关的代码定制需求&#xff0c;请点击文末卡片联系作者 文章目录 项目背景具体内容实施步骤相关例程MATLAB例程python例程 …

腾讯云AI代码助手编程挑战赛-凯撒密码解码编码器

作品简介 在CTFer选手比赛做crypto的题目时&#xff0c;一些题目需要自己去解密&#xff0c;但是解密的工具大部分在线上&#xff0c;而在比赛过程中大部分又是无网环境&#xff0c;所以根据要求做了这个工具 技术架构 python语言的tk库来完成的GUI页面设计&#xff0c;通过…

《机器学习》集成学习之随机森林

目录 一、集成学习 1、简介 2、集成学习的代表 3、XGBoost和随机森林的对比 相同点&#xff1a; 不同点&#xff1a; 二、Bagging之随机森林 1、简介 2、随机森林的核心思想 3、随机森林生成步骤 4、随机森林的优点 5、随机森林的缺点 三、随机森林的代码实现 1、…

四、VSCODE 使用GIT插件

VSCODE 使用GIT插件 一下载git插件与git Graph插件二、git插件使用三、文件提交到远程仓库四、git Graph插件 一下载git插件与git Graph插件 二、git插件使用 git插件一般VSCode自带了git&#xff0c;就是左边栏目的图标 在下载git软件后vscode的git插件会自动识别当前项目 …