首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Spring Boot + MinIO 实现分段、断点续传,让文件传输更高效

  • 25-01-18 11:43
  • 3391
  • 11022
blog.csdn.net

一、引言

在当今的互联网应用中,文件上传是一个常见的功能需求。然而,传统的文件上传方式在面对大文件或不稳定的网络环境时,可能会出现性能瓶颈和上传失败的问题。

传统文件上传,就像是用一辆小推车搬运大型家具,一次只能运送一件,不仅耗时久,还容易在途中 “翻车”,导致上传中断。而且,一旦上传失败,就得从头再来,极大地浪费了时间和资源。

为了解决这些问题,分片上传、断点续传技术应运而生。Spring Boot 作为一款流行的 Java 开发框架,提供了简洁高效的开发体验;MinIO 则是一个高性能的对象存储服务器,支持 S3 协议,能够可靠地存储文件。将它们结合使用,可以轻松实现文件的分段、断点续传功能,为用户提供更流畅的上传体验。就好比把大型家具拆分成零部件,用多辆小推车并行运输,即使某一辆车出了问题,后续也能快速从断点处继续,大大提高了运输效率。

二、实现分段上传

(一)原理剖析

        分段上传,又称分片上传,其核心原理是将大文件依照特定大小分割成一系列小块,这些小块被称为分片。随后,各个分片作为独立单元,分别通过网络上传至服务器。以传输一部高清电影为例,若电影文件大小为 5GB,按照 5MB 为一个分片进行切割,可得到 1000 个分片。在上传时,这 1000 个分片能够依次或并行地向服务器发起传输请求。相较于传统的一次性上传整个大文件,分段上传优势显著。一方面,它能极大提高上传的稳定性。在网络环境不稳定,频繁出现丢包、延迟等情况时,一次性传输大文件极易导致上传失败,而分段上传即便某个分片传输受阻,只需重新传输该分片即可,不会影响其他已成功传输的分片,有效降低了整体上传失败的风险。另一方面,分段上传支持并行操作,多个分片可同时进行上传,充分利用网络带宽资源,大大加快了上传速度。尤其在面对网络状况良好的环境,并行上传多个分片能够显著缩短大文件的上传时间,提升用户体验。

(二)MinIo使用

1.Windows安装

下载路径:MinIO下载和安装 | 用于创建高性能对象存储的代码和下载内容

CMD执行 C:\minio.exe server F:\Data --console-address ":9001"

启动后

2.Linux安装

下载地址:MinIO下载 | 中国镜像下载加速站

使用minio方式安装

wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio
chmod +x minio
MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server /mnt/data --console-address ":9001" 

使用rpm方式安装 

wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio-20241218131544.0.0-1.x86_64.rpm

rpm -ivh minio-20241218131544.0.0-1.x86_64.rpm

minio server ./

Systemd配置

.rpm软件包将以下systemd服务文件安装到/usr/lib/systemd/system/minio.service

  1. [Unit]
  2. Description=MinIO
  3. Documentation=https://min.io/docs/minio/linux/index.html
  4. Wants=network-online.target
  5. After=network-online.target
  6. AssertFileIsExecutable=/usr/local/bin/minio
  7. [Service]
  8. WorkingDirectory=/usr/local
  9. User=minio-user
  10. Group=minio-user
  11. ProtectProc=invisible
  12. EnvironmentFile=-/etc/default/minio
  13. ExecStartPre=/bin/bash -c "if [ -z \"${MINIO_VOLUMES}\" ]; then echo \"Variable MINIO_VOLUMES not set in /etc/default/minio\"; exit 1; fi"
  14. ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
  15. # MinIO RELEASE.2023-05-04T21-44-30Z adds support for Type=notify (https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=)
  16. # This may improve systemctl setups where other services use `After=minio.server`
  17. # Uncomment the line to enable the functionality
  18. # Type=notify
  19. # Let systemd restart this service always
  20. Restart=always
  21. # Specifies the maximum file descriptor number that can be opened by this process
  22. LimitNOFILE=65536
  23. # Specifies the maximum number of threads this process can create
  24. TasksMax=infinity
  25. # Disable timeout logic and wait until process is stopped
  26. TimeoutStopSec=infinity
  27. SendSIGKILL=no
  28. [Install]
  29. WantedBy=multi-user.target
  30. # Built for ${project.name}-${project.version} (${project.name})

默认情况下,minio.service文件作为minio-user用户和组运行。您可以使用groupadd和useradd命令创建用户和组。以下示例创建用户和组,并设置访问MinIO要使用的文件夹路径的权限。这些命令通常需要root(sudo)权限。 

groupadd -r minio-user
useradd -M -r -g minio-user minio-user
chown minio-user:minio-user /mnt/data

 创建环境变量文件

在/etc/default/minio创建一个环境变量文件。MinIO Server容器可以使用此文件作为所有环境变量的源。

​

  1. MINIO_OPTS="--address '0.0.0.0:9050' --console-address '0.0.0.0:9051' "
  2. MINIO_VOLUMES="/data/minio/data"
  3. MINIO_ROOT_USER=minio
  4. MINIO_ROOT_PASSWORD=minio123

最后 sudo systemctl start minio.service 启动minio服务

常用命令

启动MinIO

systemctl start minio
查询运行状态

systemctl status minio

停止MinIO

systemctl stop minio

 3.minio配置

登录minio web端  

创建桶

​

​ 

​ 

​ 

创建Key

(三)核心代码解读

  1. 配置 MinIO 客户端:在 Spring Boot 项目中引入 MinIO 依赖,首先需在 pom.xml 文件中添加相应依赖项:
  1. <dependency>
  2. <groupId>io.miniogroupId>
  3. <artifactId>minioartifactId>
  4. <version>8.4.4version>
  5. dependency>

此处以 8.4.4 版本为例,实际使用时应根据 MinIO 官方发布的稳定版本进行调整。引入依赖后,接着在 application.yml 或 application.properties 配置文件中配置 MinIO 连接信息:

  1. minio:
  2. endpoint: http://localhost:9000 //minio api地址端口
  3. access-key: accessKey //minio access-key
  4. secret-key: secretKey //minio secretKey
  5. bucket-name: your-bucket-name //桶

上述配置中,endpoint指定 MinIO 服务的访问地址,access-key与secret-key是访问 MinIO 服务器的凭证,用于身份验证,bucket-name则是文件存储的桶名称。通过这些配置,Spring Boot 项目便能顺利与 MinIO 服务器建立连接,为后续文件上传操作奠定基础。

  1. 文件分片上传逻辑:以下是一段基于 Spring Boot 结合 MinIO 实现文件分片上传的关键代码片段:
  1. import io.minio.MinioClient;
  2. import io.minio.PutObjectArgs;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestParam;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import org.springframework.web.multipart.MultipartFile;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. @RestController
  11. public class FileUploadController {
  12. @Autowired
  13. private MinioClient minioClient;
  14. // 定义分片大小,这里设为5MB,可根据实际情况调整
  15. private static final long CHUNK_SIZE = 5 * 1024 * 1024;
  16. @PostMapping("/upload")
  17. public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
  18. String bucketName = "your-bucket-name";
  19. String objectName = file.getOriginalFilename();
  20. InputStream inputStream = file.getInputStream();
  21. long fileSize = file.getSize();
  22. // 计算分片数量
  23. long totalChunks = fileSize % CHUNK_SIZE == 0? fileSize / CHUNK_SIZE : fileSize / CHUNK_SIZE + 1;
  24. for (int i = 0; i < totalChunks; i++) {
  25. long offset = i * CHUNK_SIZE;
  26. long chunkSize = Math.min(CHUNK_SIZE, fileSize - offset);
  27. byte[] buffer = new byte[(int) chunkSize];
  28. inputStream.read(buffer);
  29. // 构建上传参数,上传每个分片
  30. PutObjectArgs args = PutObjectArgs.builder()
  31. .bucket(bucketName)
  32. .object(objectName + "_chunk_" + i)
  33. .stream(new ByteArrayInputStream(buffer), chunkSize, -1)
  34. .build();
  35. try {
  36. minioClient.putObject(args);
  37. } catch (Exception e) {
  38. // 处理上传分片失败的情况,可记录日志、重试等
  39. e.printStackTrace();
  40. }
  41. }
  42. return "File uploaded successfully";
  43. }
  44. }

        在这段代码中,首先通过@Autowired注解注入MinioClient,确保能够与 MinIO 服务器进行交互。接着定义了分片大小CHUNK_SIZE,这里设定为 5MB,开发者可依据文件特性、网络带宽等因素灵活调整。在uploadFile方法中,获取上传的文件信息,包括文件名、输入流以及文件大小,并根据分片大小计算出分片总数。随后,通过循环依次读取每个分片的数据,利用MinioClient的putObject方法将分片上传至指定的存储桶中。若上传过程中某个分片出现异常,代码中简单地进行了打印堆栈信息的处理,实际应用中,可根据需求进一步优化,如记录详细日志、尝试自动重试上传等,以增强系统的可靠性与稳定性。

四、实现断点续传

(一)原理剖析

        断点续传是在分片上传基础上的一项关键优化,其核心原理在于精准记录上传进度,并在遭遇网络或操作中断后,能够从断点处继续上传。当文件开始分片上传时,系统会同步记录已成功上传的分片信息,常见的记录方式包括在数据库中存储已上传分片的序号、使用 Redis 等缓存工具记录上传状态等。一旦上传过程中断,无论是由于网络故障、设备断电还是用户主动暂停,下次上传时,系统首先会查询记录的上传进度,识别出已上传的分片,随后仅传输尚未完成的分片,避免了重复劳动。这种机制极大地提升了用户体验,特别是在上传大文件时,若因意外中断而需从头开始,将耗费大量时间与资源,而断点续传则有效解决了这一痛点,让文件上传更加智能、高效。

(二)核心代码解读

  1. 记录上传进度:以下是一段使用 Redis 记录上传进度的示例代码:
  1. import redis.clients.jedis.Jedis;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class UploadProgressRecorder {
  6. @Autowired
  7. private Jedis jedis;
  8. public void recordProgress(String uploadId, int chunkIndex) {
  9. jedis.sadd(uploadId, String.valueOf(chunkIndex));
  10. }
  11. public boolean isChunkUploaded(String uploadId, int chunkIndex) {
  12. return jedis.sismember(uploadId, String.valueOf(chunkIndex));
  13. }
  14. public void close() {
  15. jedis.close();
  16. }
  17. }

在这段代码中,UploadProgressRecorder类负责与 Redis 交互,记录和查询上传进度。通过@Autowired注入Jedis实例,recordProgress方法使用SADD命令将已上传的分片索引添加到以uploadId为键的集合中,isChunkUploaded方法则借助SISMEMBER命令判断指定分片是否已上传,最后提供close方法关闭 Redis 连接,确保资源的正确释放。这里使用集合数据结构来存储已上传分片索引,能够方便地进行成员判断与批量操作,适用于频繁的上传进度记录与查询场景。

  1. 续传逻辑实现:在文件上传控制器中,结合上述记录进度的功能,实现断点续传逻辑:
  1. import io.minio.MinioClient;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.web.bind.annotation.PostMapping;
  4. import org.springframework.web.bind.annotation.RequestParam;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import org.springframework.web.multipart.MultipartFile;
  7. import java.io.IOException;
  8. import java.util.Set;
  9. @RestController
  10. public class FileUploadController {
  11. @Autowired
  12. private MinioClient minioClient;
  13. @Autowired
  14. private UploadProgressRecorder progressRecorder;
  15. // 定义分片大小,这里设为5MB,可根据实际情况调整
  16. private static final long CHUNK_SIZE = 5 * 1024 * 1024;
  17. @PostMapping("/upload")
  18. public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
  19. String bucketName = "your-bucket-name";
  20. String objectName = file.getOriginalFilename();
  21. String uploadId = generateUploadId(); // 生成唯一的上传ID,用于标识本次上传任务
  22. long fileSize = file.getSize();
  23. long totalChunks = fileSize % CHUNK_SIZE == 0? fileSize / CHUNK_SIZE : fileSize / CHUNK_SIZE + 1;
  24. // 检查已上传的分片
  25. Set uploadedChunks = progressRecorder.getUploadedChunks(uploadId);
  26. for (int i = 0; i < totalChunks; i++) {
  27. if (!uploadedChunks.contains(String.valueOf(i))) {
  28. long offset = i * CHUNK_SIZE;
  29. long chunkSize = Math.min(CHUNK_SIZE, fileSize - offset);
  30. byte[] buffer = new byte[(int) chunkSize];
  31. file.getInputStream().read(buffer);
  32. // 构建上传参数,上传未完成的分片
  33. PutObjectArgs args = PutObjectArgs.builder()
  34. .bucket(bucketName)
  35. .object(objectName + "_chunk_" + i)
  36. .stream(new ByteArrayInputStream(buffer), chunkSize, -1)
  37. .build();
  38. try {
  39. minioClient.putObject(args);
  40. progressRecorder.recordProgress(uploadId, i); // 记录分片上传成功
  41. } catch (Exception e) {
  42. // 处理上传分片失败的情况,可记录日志、重试等
  43. e.printStackTrace();
  44. }
  45. }
  46. }
  47. // 判断所有分片是否都已上传完成,若完成则可进行合并操作或其他后续处理
  48. if (uploadedChunks.size() == totalChunks) {
  49. // 执行合并分片等操作
  50. return "File uploaded and merged successfully";
  51. }
  52. return "Upload paused, will resume from breakpoint next time";
  53. }
  54. }

在上述uploadFile方法中,首先生成一个唯一的上传 ID,用于在后续操作中准确标识本次上传任务。接着,依据文件大小与预设的分片大小计算出总分片数。随后,通过progressRecorder查询已上传的分片集合,在循环上传分片时,跳过已存在于集合中的分片,仅上传尚未完成的部分,并在每次成功上传后,及时记录该分片的上传状态。最后,通过对比已上传分片数量与总分片数,判断文件是否全部上传完毕,若完成则可触发后续的合并操作或进行相应的业务处理,若未完成,则告知用户上传处于暂停状态,下次将从断点处继续。如此一来,系统便能在面对复杂多变的网络环境与用户操作时,稳定、高效地实现文件的断点续传功能。

五、优化与拓展

在实现了基本的分段、断点续传功能后,还可以从多个方面对文件上传系统进行优化与拓展,进一步提升性能与用户体验。

(一)上传性能优化

  1. 调整分片大小:分片大小的选择对上传性能有着显著影响。若分片过大,在网络不稳定时,单个分片传输失败的概率会增加,且重传成本高;若分片过小,会产生过多的网络请求,增加服务器的处理开销与延迟。一般而言,可依据网络带宽的稳定程度、服务器的处理能力以及文件类型来动态调整分片大小。例如,在网络带宽充裕且稳定时,适当增大分片大小,以减少请求次数,提高传输效率;反之,在网络波动较大时,减小分片大小,降低单个分片传输失败的风险。通过实时监测网络状况,结合文件大小,智能地选择最优分片大小,能够极大提升上传的稳定性与速度。
  1. 优化网络请求:在文件上传过程中,减少不必要的网络请求开销至关重要。一方面,可采用连接池技术,复用已建立的网络连接,避免频繁地创建与销毁连接带来的性能损耗。例如,在 Spring Boot 项目中配置合适的连接池参数,如最大连接数、最小空闲连接数等,确保在高并发上传场景下,网络连接资源能够得到高效利用。另一方面,对网络请求进行批量处理,将多个小的上传请求合并为一个大的请求,减少请求头、响应头等额外信息的传输开销,提高网络传输的有效负载率。同时,结合异步编程模型,让文件上传操作在后台线程执行,避免阻塞主线程,提升系统的响应性,使得用户在上传文件时,仍能流畅地进行其他操作。

(二)功能拓展思路

  1. 结合秒传功能:秒传功能是提升用户上传体验的一大利器。其原理是在上传文件之前,先计算文件的哈希值(如常用的 MD5、SHA-256 等算法),然后将该哈希值发送至服务器。服务器依据此哈希值在存储系统中快速检索,判断是否已存在相同哈希值的文件。若存在,则直接返回该文件的访问链接,用户无需重复上传实际的文件内容,瞬间完成 “上传” 操作。这对于一些常见的、已在服务器存储过的文件,如常用的图片、文档模板等,能够极大地节省上传时间与带宽资源。在结合 Spring Boot 与 MinIO 实现时,可在文件上传控制器中新增一个接口,用于接收文件哈希值并进行秒传判断。若文件不存在,则按照常规的分段、断点续传流程进行上传,从而实现秒传与分段上传的无缝融合。
  1. 增加文件校验:为确保上传文件的完整性与准确性,文件校验必不可少。除了在断点续传中记录已上传分片信息,防止重复上传外,还可在上传完成后,对整个文件进行校验。一种常见的方式是在客户端计算文件的哈希值,并在上传完成后,将该哈希值与服务器端重新计算的文件哈希值进行比对。若两者一致,则表明文件在传输过程中未发生损坏或篡改;若不一致,则提示用户文件可能存在问题,需要重新上传。此外,还可引入数据冗余技术,如在 MinIO 存储中采用纠删码(Erasure Coding),即使部分数据丢失或损坏,仍能通过冗余信息恢复文件,进一步提高数据的可靠性,为用户提供更加安全、可靠的文件上传服务。

七、总结

通过 Spring Boot 与 MinIO 的强强联合,我们成功实现了文件的分段、断点续传功能,有效解决了大文件上传在不稳定网络环境下的难题。这一技术方案不仅提升了文件上传的稳定性与效率,还为用户带来了更加流畅的使用体验。在实际应用中,开发者可根据具体业务场景,灵活调整分片大小、优化网络请求,进一步拓展秒传、文件校验等功能,满足多样化的需求。未来,随着云计算与大数据技术的不断发展,相信这一技术组合将在更多领域发挥重要作用,如视频直播、云存储服务、大数据分析等,为海量数据的高效传输与处理提供坚实保障,助力企业实现数字化转型与升级。

2024年的最后一篇文章 在这祝大家新年快乐,一夜暴富!!!

注:本文转载自blog.csdn.net的咕德猫宁丶的文章"https://blog.csdn.net/cauyaycau/article/details/144841565"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

103
后端
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top