一、引入依赖

		<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.3.3</version></dependency>

二、自定义Minio客户端

package com.gstanzer.video.controller;import com.google.common.collect.Multimap;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Part;import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;public class CustomMinioClient extends MinioClient {/*** 继承父类*/public CustomMinioClient(MinioClient client) {super(client);}/*** 初始化分片上传即获取uploadId*/public String initMultiPartUpload(String bucket, String region, String object, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, ServerException, InternalException, XmlParserException, InvalidResponseException, ErrorResponseException {CreateMultipartUploadResponse response = this.createMultipartUpload(bucket, region, object, headers, extraQueryParams);return response.result().uploadId();}/*** 上传单个分片*/public UploadPartResponse uploadMultiPart(String bucket, String region, String object, Object data,long length,String uploadId,int partNumber,Multimap<String, String> headers,Multimap<String, String> extraQueryParams) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, ServerException, InternalException, XmlParserException, InvalidResponseException, ErrorResponseException {return this.uploadPart(bucket, region, object, data, length, uploadId, partNumber, headers, extraQueryParams);}/*** 合并分片*/public ObjectWriteResponse mergeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws IOException, NoSuchAlgorithmException, InsufficientDataException, ServerException, InternalException, XmlParserException, InvalidResponseException, ErrorResponseException, ServerException, InvalidKeyException {return this.completeMultipartUpload(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);}public void cancelMultipartUpload(String bucketName, String region, String objectName, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, IOException, InvalidKeyException, XmlParserException, InvalidResponseException, InternalException {this.abortMultipartUpload(bucketName, region, objectName, uploadId, extraHeaders, extraQueryParams);}/*** 查询当前上传后的分片信息*/public ListPartsResponse listMultipart(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {return this.listParts(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);}
}

三、分片上传核心完整代码

1.实体类

package com.gstanzer.video.form;import lombok.Data;/*** @author: tangbingbing* @date: 2025/6/4 08:27*/
@Data
public class UploadPartForm {// 分片文件路径拼接字符串(比如:E:\\scdx\\test\\test.doc.part1,E:\\scdx\\test\\test.doc.part2)private String partFilePaths;// 分片文件urlprivate String partFileUrl;// 上传idprivate String uploadId;// 文件类型(word类型:application/msword,安卓安装包类型:application/vnd.android.package-archive)private String contentType;
}

2.控制层

package com.gstanzer.video.controller;import com.alibaba.fastjson.JSON;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.gstanzer.video.form.UploadPartForm;
import com.gstanzer.video.swagger.ApiForBackEndInVideo;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Part;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;@Slf4j
@RestController
@RequestMapping("/minio")
public class MinioController {private CustomMinioClient minioClient;@Value("${minio.endpoint}")private String endpoint;@Value("${minio.bucketName}")private String bucketName;@Value("${minio.access-key}")private String accessKey;@Value("${minio.secret-key}")private String secretKey;@PostConstructpublic void init() throws Exception {minioClient = new CustomMinioClient(MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build());boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if (!found) {log.info("Not found minio bucket: {}, auto create it now", bucketName);minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());log.info("Auto create minio bucket: {} {}", bucketName, found);} else {log.info("Found minio bucket: {}", bucketName);}}/*** 第一步,简单用一个10MB出头的文件,按5MB分片大小进行分片*/public static void main(String[] args) throws Exception {long CHUNK_SIZE = 15 * 1024 * 1024;// 将文件分片存储String filePath = "E:\\scdx\\app\\xfgj_v3.0.88.apk";File file = new File(filePath);long fileSize = file.length();int chunkCount = (int) Math.ceil((double) fileSize / CHUNK_SIZE);log.info("Created: " + chunkCount + " chunks");try (FileInputStream fis = new FileInputStream(file)) {byte[] buffer = new byte[(int) CHUNK_SIZE];for (int i = 0; i < chunkCount; i++) {String chunkFileName = filePath + ".part" + (i + 1);try (FileOutputStream fos = new FileOutputStream(chunkFileName)) {int bytesRead = fis.read(buffer);fos.write(buffer, 0, bytesRead);log.info("Created: " + chunkFileName + " (" + bytesRead + " bytes)");}}} catch (IOException e) {e.printStackTrace();}}/*** 第二步,申请一个大文件上传*/@ApiForBackEndInVideo@PostMapping("/uploadLargeFile/apply")public ResponseEntity<Map> applyUploadPsiResult2Minio(@RequestParam("largeFileName") String largeFileName,@RequestParam("chunkCount") Integer chunkCount) {String uploadId = getUploadId(largeFileName, null);Map<String, Object> map = new HashMap<>();map.put("uploadId", uploadId);Map<String, String> reqParams = new HashMap<>();reqParams.put("uploadId", uploadId);List<String> uploadUrlList = new ArrayList<>();for (int i = 1; i <= chunkCount; i++) {reqParams.put("partNumber", String.valueOf(i));String uploadUrl = getPresignedObjectUrl(largeFileName, reqParams);uploadUrlList.add(uploadUrl);}map.put("chunkUploadUrls", uploadUrlList);return ResponseEntity.ok(map);}/*** 准备分片上传时,在此先获取上传任务id*/private String getUploadId(String objectName, String contentType){log.info("tip message: 通过 <{}-{}-{}> 开始初始化<分片上传>数据", objectName, contentType, bucketName);if (StringUtils.isBlank(contentType)) {contentType = "application/octet-stream";}HashMultimap<String, String> headers = HashMultimap.create();headers.put("Content-Type", contentType);try {return minioClient.initMultiPartUpload(bucketName, null, objectName, headers, null);} catch (Exception e) {throw new RuntimeException("获取uploadId失败", e);}}/*** 请求分片上传的url*/private String getPresignedObjectUrl(String fileName, Map<String, String> reqParams) {try {String presignedObjectUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.PUT).bucket(bucketName).object(fileName).expiry(1, TimeUnit.DAYS).extraQueryParams(reqParams).build());return presignedObjectUrl;} catch (Exception e) {throw new RuntimeException("getPresignedObjectUrl failed", e);}}/*** 第三步,每个分片逐个通过签名后的分片上传url,进行上传,未合并之前都可以重复覆盖上传* 在video服务的控制台通过如下命令将分片下载到video服务中* wget http://100.86.2.1:80/xfaq/xfgj_v3.0.88.apk.part1*/@ApiForBackEndInVideo@PostMapping("/uploadLargeFile/uploadPart")public ResponseEntity<String> applyUploadPsiResult2Minio(@RequestBody UploadPartForm form) {String partFilePaths = form.getPartFilePaths();String partFileUrl = form.getPartFileUrl();try {// 第一步得到的分片文件List<String> chunkFilePaths = Lists.newArrayList(partFilePaths.split(","));// 第二步得到的上传url信息Map<String, Object> uploadIdMap = new HashMap<>();uploadIdMap.put("uploadId", form.getUploadId());List<String> chunkUploadUrls = Lists.newArrayList(partFileUrl.split(","));uploadIdMap.put("chunkUploadIds", chunkUploadUrls);// 基于此,分片直接上传到minio,不走服务端,省去一次网络IO开销HttpClient httpClient = HttpClients.createDefault();for (int i = 0; i < chunkUploadUrls.size(); i++) {// PUT直接上传到minioString chunkUploadId = chunkUploadUrls.get(i);HttpPut httpPut = new HttpPut(chunkUploadId);httpPut.setHeader("Content-Type",form.getContentType());File chunkFile = new File(chunkFilePaths.get(i));FileEntity chunkFileEntity = new FileEntity(chunkFile);httpPut.setEntity(chunkFileEntity);HttpResponse chunkUploadResp = httpClient.execute(httpPut);log.info("[分片" + (i + 1) + "]上传响应:" + JSON.toJSONString(chunkUploadResp));httpPut.releaseConnection();}return ResponseEntity.ok("上传成功!");} catch (IOException e) {e.printStackTrace();throw new RuntimeException(e);}}public static void main3(String[] args) throws Exception {// 第一步得到的分片文件List<String> chunkFilePaths = Lists.newArrayList("E:\\scdx\\test\\test.doc.part1","E:\\scdx\\test\\test.doc.part2");// 第二步得到的上传url信息Map<String, Object> uploadIdMap = new HashMap<>();uploadIdMap.put("uploadId", "YTJlOWZhMmEtM2I3My00MmIyLWE0YjgtMDFkYjQzMzIyNmVhLjFmYzQ2M2ViLTI2YmYtNDZjMi04M2ZlLWJjMThjOTk0MWU4OHgxNzQ4OTM3NDE3MjI4ODA1MTAw");List<String> chunkUploadUrls = Lists.newArrayList("http://127.0.0.1:9000/xfaq/test.doc?uploadId=YTJlOWZhMmEtM2I3My00MmIyLWE0YjgtMDFkYjQzMzIyNmVhLjFmYzQ2M2ViLTI2YmYtNDZjMi04M2ZlLWJjMThjOTk0MWU4OHgxNzQ4OTM3NDE3MjI4ODA1MTAw&partNumber=1&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20250603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250603T075657Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=649af2001e096f422479e139efc4aab51301061c026f40c0685cd20edbb28211","http://127.0.0.1:9000/xfaq/test.doc?uploadId=YTJlOWZhMmEtM2I3My00MmIyLWE0YjgtMDFkYjQzMzIyNmVhLjFmYzQ2M2ViLTI2YmYtNDZjMi04M2ZlLWJjMThjOTk0MWU4OHgxNzQ4OTM3NDE3MjI4ODA1MTAw&partNumber=2&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20250603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250603T075657Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedd5431a6e4ba0ebda1c2b227f7543eed77d245b3d86c6d5d505d27f61cd823");uploadIdMap.put("chunkUploadIds", chunkUploadUrls);// 基于此,分片直接上传到minio,不走服务端,省去一次网络IO开销HttpClient httpClient = HttpClients.createDefault();for (int i = 0; i < chunkUploadUrls.size(); i++) {// PUT直接上传到minioString chunkUploadId = chunkUploadUrls.get(i);HttpPut httpPut = new HttpPut(chunkUploadId);httpPut.setHeader("Content-Type","application/msword");File chunkFile = new File(chunkFilePaths.get(i));FileEntity chunkFileEntity = new FileEntity(chunkFile);httpPut.setEntity(chunkFileEntity);HttpResponse chunkUploadResp = httpClient.execute(httpPut);log.info("[分片" + (i + 1) + "]上传响应:" + JSON.toJSONString(chunkUploadResp));httpPut.releaseConnection();}}/*** 第四步,合并分片*/@ApiForBackEndInVideo@PostMapping("/uploadLargeFile/merge")public ResponseEntity<Boolean> applyUploadPsiResult2Minio(@RequestParam("largeFileName") String largeFileName,@RequestParam("uploadId") String uploadId) {boolean mergeResult = mergeMultipartUpload(largeFileName, uploadId);return ResponseEntity.ok(mergeResult);}/*** 分片上传完后合并* @param objectName 文件名称* @param uploadId* @return*/public boolean mergeMultipartUpload(String objectName, String uploadId) {try {log.info("ready to merge <" + objectName + " - " + uploadId + " - " + bucketName + ">");// 查询上传后的分片数据ListPartsResponse partResult = minioClient.listMultipart(bucketName, null, objectName, 1000, 0, uploadId, null, null);int chunkCount = partResult.result().partList().size();Part[] parts = new Part[chunkCount];int partNumber = 1;for (Part part : partResult.result().partList()) {parts[partNumber - 1] = new Part(partNumber, part.etag());partNumber++;}// 合并分片ObjectWriteResponse objectWriteResponse = minioClient.mergeMultipartUpload(bucketName, null, objectName, uploadId, parts, null, null);log.info("mergeMultipartUpload resp: {}", objectWriteResponse);} catch (Exception e) {log.error("合并失败:", e);return false;}return true;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87635.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87635.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/87635.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jenkins 插件深度应用:让你的CI/CD流水线如虎添翼 [特殊字符]

Jenkins 插件深度应用&#xff1a;让你的CI/CD流水线如虎添翼 &#x1f680; 嘿&#xff0c;各位开发小伙伴&#xff01;今天咱们来聊聊Jenkins的插件生态系统。如果说Jenkins是一台强大的引擎&#xff0c;那插件就是让这台引擎发挥最大威力的各种零部件。准备好了吗&#xff1…

密码学(斯坦福)

密码学笔记 \huge{密码学笔记} 密码学笔记 斯坦福大学密码学的课程笔记 课程网址&#xff1a;https://www.bilibili.com/video/BV1Rf421o79E/?spm_id_from333.337.search-card.all.click&vd_source5cc05a038b81f6faca188e7cf00484f6 概述 密码学的使用背景 安全信息保护…

代码随想录算法训练营第四十六天|动态规划part13

647. 回文子串 题目链接&#xff1a;647. 回文子串 - 力扣&#xff08;LeetCode&#xff09; 文章讲解&#xff1a;代码随想录 思路&#xff1a; 以dp【i】表示以s【i】结尾的回文子串的个数&#xff0c;发现递推公式推导不出来此路不通 以dp【i】【j】表示s【i】到s【j】的回…

基于四种机器学习算法的球队数据分析预测系统的设计与实现

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍项目展示随机森林模型XGBoost模型逻辑回归模型catboost模型每文一语 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 本项目旨在设计与实现…

http、SSL、TLS、https、证书

一、基础概念 1.HTTP HTTP (超文本传输协议) 是一种用于客户端和服务器之间传输超媒体文档的应用层协议&#xff0c;是万维网的基础。 简而言之&#xff1a;一种获取和发送信息的标准协议 2.SSL 安全套接字层&#xff08;SSL&#xff09;是一种通信协议或一组规则&#xf…

在 C++ 中,判断 `std::string` 是否为空字符串

在 C 中&#xff0c;判断 std::string 是否为空字符串有多种方法&#xff0c;以下是最常用的几种方式及其区别&#xff1a; 1. 使用 empty() 方法&#xff08;推荐&#xff09; #include <string>std::string s; if (s.empty()) {// s 是空字符串 }特性&#xff1a; 时间…

【Harmony】鸿蒙企业应用详解

【HarmonyOS】鸿蒙企业应用详解 一、前言 1、应用类型定义速览&#xff1a; HarmonyOS目前针对应用分为三种类型&#xff1a;普通应用&#xff0c;游戏应用&#xff0c;企业应用。 而企业应用又分为&#xff0c;企业普通应用和设备管理应用MDM&#xff08;Mobile Device Man…

Linux云计算基础篇(8)

VIM 高级特性插入模式按 i 进入插入模式。按 o 在当前行下方插入空行并进入插入模式。按 O 在当前行上方插入空行并进入插入模式。命令模式:set nu 显示行号。:set nonu 取消显示行号。:100 光标跳转到第 100 行。G 光标跳转到文件最后一行。gg 光标跳转到文件第一行。30G 跳转…

Linux进程单例模式运行

Linux进程单例模式运行 #include <iostream> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int write_pid(const cha…

【Web 后端】部署服务到服务器

文章目录 前言一、如何启动服务二、挂载和开机启动服务1. 配置systemctl 服务2. 创建server用户3. 启动服务 总结 前言 如果你的后端服务写好了如果部署到你的服务器呢&#xff0c;本次通过fastapi写的服务实例&#xff0c;示范如何部署到服务器&#xff0c;并做服务管理。 一…

国产MCU学习Day5——CW32F030C8T6:窗口看门狗功能全解析

每日更新教程&#xff0c;评论区答疑解惑&#xff0c;小白也能变大神&#xff01;" 目录 一.窗口看门狗&#xff08;WWDG&#xff09;简介 二.窗口看门狗寄存器列表 三.窗口看门狗复位案例 一.窗口看门狗&#xff08;WWDG&#xff09;简介 CW32F030C8T6 内部集成窗口看…

2025年文件加密软件分享:守护数字世界的核心防线

在数字化时代&#xff0c;数据已成为个人与企业的宝贵资产&#xff0c;文件加密软件通过复杂的算法&#xff0c;确保信息在存储、传输与共享过程中的保密性、完整性与可用性。一、文件加密软件的核心原理文件加密软件算法以其高效性与安全性广泛应用&#xff0c;通过对文件数据…

node.js下载教程

1.项目环境文档 语雀 2.nvm安装教程与nvm常见命令,超详细!-阿里云开发者社区 C:\Windows\System32>nvm -v 1.2.2 C:\Windows\System32>nvm list available Error retrieving "http://npm.taobao.org/mirrors/node/index.json": HTTP Status 404 C:\Window…

(AI如何解决问题)在一个项目,跳转到外部html页面,页面布局

问题描述目前&#xff0c;ERP后台有很多跳转外部链接的地方&#xff0c;会直接打开一个tab显示。因为有些页面是适配手机屏幕显示&#xff0c;放在浏览器会超级大。不美观&#xff0c;因此提出优化。修改前&#xff1a;修改后&#xff1a;思考过程1、先看下代码&#xff1a;log…

网络通信协议与虚拟网络技术相关整理(上)

#作者&#xff1a;程宏斌 文章目录 tcp协议udp协议arp协议icmp协议dhcp协议BGP协议OSPF协议BGP vs OSPF 对比表VLAN&#xff08;Virtual LAN&#xff09;VXLAN&#xff08;Virtual Extensible LAN&#xff09;IPIP&#xff08;IP-in-IP&#xff09;vxlan/vlan/ipip网桥/veth网…

物联网软件层面的核心技术体系

物联网软件层面的核心技术体系 物联网(IoT)软件技术栈是一个多层次的复杂体系&#xff0c;涵盖从设备端到云平台的完整解决方案。以下是物联网软件层面的关键技术分类及详细说明&#xff1a; 一、设备端软件技术 1. 嵌入式操作系统 实时操作系统(RTOS)&#xff1a; FreeRTO…

GreatSQL通过伪装从库回放Binlog文件

GreatSQL通过伪装从库回放Binlog文件 一、适用场景说明 1、主库误操作恢复 利用 Binlog 在其他实例解析、回放&#xff0c;根据gtid只回放到指定位点。 2、网络隔离环境同步 备份恢复后可以拉去主库Binlog文件至新实例同步增量数据。 3、备份恢复遇到Binlog文件过大处理 恢复实…

MVC 架构设计模式

在现代软件开发中&#xff0c;架构设计决定了一个项目的可维护性与可扩展性。MVC&#xff08;Model-View-Controller&#xff09;作为经典的分层设计模式&#xff0c;广泛应用于 Web 系统、前端应用乃至移动端开发中。本文不仅介绍 MVC 的核心思想和机制&#xff0c;还将结合具…

(18)python+playwright自动化测试鼠标拖拽-上

1.简介 本文主要介绍两个在测试过程中可能会用到的功能&#xff1a;在selenium中介绍了Actions类中的拖拽操作和Actions类中的划取字段操作。例如&#xff1a;需要在一堆log字符中随机划取一段文字&#xff0c;然后右键选择摘取功能。playwright同样可以实现元素的拖拽和释放的…

Android 网络全栈攻略(四)—— TCPIP 协议族与 HTTPS 协议

Android 网络全栈攻略系列文章&#xff1a; Android 网络全栈攻略&#xff08;一&#xff09;—— HTTP 协议基础 Android 网络全栈攻略&#xff08;二&#xff09;—— 编码、加密、哈希、序列化与字符集 Android 网络全栈攻略&#xff08;三&#xff09;—— 登录与授权 Andr…