[toc]
MinIO 笔记 2
SpringBoot 与 minio 搭配使用
- 先创建 springboot 工程。
- 导入 minio 客户端依赖
xml
<!-- MinIO 客户端 -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.2</version>
</dependency>
- minio 配置
yml
minio:
url: http://127.0.0.1:19000 # minio服务端地址
accessKey: minioadmin # 用户名
secretKey: minioadmin # 密码
- 自定义 minio 客户端类
如果想要实现分片文件上传。我们需要用到下面的方法。但是这几个方法,MinioClient类无法调用。我们必须自定义MinioClient类,让自定义MinioClient类去继承官方的MinioClient类之后,自定义MinioClient类才能主动调用这几个方法。
注意此处用到的minio依赖包是8.2.2版本的。
createMultipartUpload //创建文件上传id
listParts //获取分片上传列表
completeMultipartUpload // 合并分片文件
java
@Component
public class MyMinioClient extends MinioClient {
/**
* 调用父类MinioClient的构造方法
* @param client
*/
public MyMinioClient(MinioClient client) {
super(client);
}
/**
* 创建分片上传请求
*
* @param bucketName 存储桶
* @param region 区域
* @param objectName 对象名
* @param headers 消息头
* @param extraQueryParams 额外查询参数
*/
@Override
public CreateMultipartUploadResponse createMultipartUpload(String bucketName, String region, String objectName, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) {
return super.createMultipartUpload(bucketName, region, objectName, headers, extraQueryParams);
}
/**
* 完成分片上传,执行合并文件
*
* @param bucketName 存储桶
* @param region 区域
* @param objectName 对象名
* @param uploadId 上传ID
* @param parts 分片
* @param extraHeaders 额外消息头
* @param extraQueryParams 额外查询参数
*/
@Override
public ObjectWriteResponse completeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) {
return super.completeMultipartUpload(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);
}
/**
* 查询分片文件的数据
* @param bucketName 存储桶
* @param region 区域
* @param objectName 对象名
* @param uploadId 上传ID
* @param extraHeaders 额外消息头
* @param extraQueryParams 额外查询参数
*/
public ListPartsResponse listParts(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) {
return super.listParts(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);
}
}
- minio 配置类
minio 配置类中注入我们自定义 MinioClient 类。
java
@Configuration
public class MinioConfig {
/**
* 访问地址
*/
@Value("${minio.url}")
private String endpoint;
/**
* accessKey类似于用户ID,用于唯一标识你的账户
*/
@Value("${minio.accessKey}")
private String accessKey;
/**
* secretKey是你账户的密码
*/
@Value("${minio.secretKey}")
private String secretKey;
@Bean
public MyMinioClient minioClient() {
MinioClient minioClient = MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
return new MyMinioClient(minioClient);
}
}
- minio 工具类
minio 工具类中使用我们自定义的 MinioClient 类。
java
/**
* MinIO工具类
*/
@Slf4j
@Component
public class MinioUtils {
@Autowired
private MyMinioClient myMinioClient;
/****************************** 操作 Bucket ******************************/
/**
* 创建存储bucket
* @return Boolean
*/
public void makeBucket(String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
myMinioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
/**
* 判断Bucket是否存在,true:存在,false:不存在
* @param bucketName
* @return
*/
public boolean bucketExists(String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return myMinioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}
/**
* 查询所有存储桶Bucket
* @return Bucket列表集合
*/
public List<Bucket> getBucketslist() throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return myMinioClient.listBuckets();
}
/**
* 根据bucketName删除Bucket,true:删除成功; false:删除失败或桶中存在文件
* @param bucketName
* @throws Exception
*/
public void removeBucket(String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
myMinioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
/****************************** 查询文件 ******************************/
/**
* 判断文件是否存在
* @param bucketName
* @param objectName
* @return
*/
public boolean isObjectExist(String bucketName, String objectName) {
boolean exist = true;
try {
myMinioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
} catch (Exception e) {
log.error("[Minio工具类]>>>> 判断文件是否存在, 异常:", e);
exist = false;
}
return exist;
}
/**
* 获取文件信息, 如果抛出异常则说明文件不存在
* @param bucketName 存储桶
* @param objectName 文件名称
* @return
*/
public String getFileInfo(String bucketName, String objectName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return myMinioClient.statObject(
StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build()).toString();
}
/**
* 根据文件前缀来获取文件列表信息
* @param bucketName 存储桶
* @param prefix 文件名称前缀
* @param recursive 是否递归查找,false:模拟文件夹结构查找
* @return 二进制流
*/
public Iterable<Result<Item>> getListObjects(String bucketName, String prefix, boolean recursive) {
return myMinioClient.listObjects(
ListObjectsArgs.builder()
.bucket(bucketName)
.prefix(prefix)
.recursive(recursive)
.build());
}
/**
* 根据文件前缀,查询文件
* @param bucketName 存储桶
* @param prefix 前缀
* @param recursive 是否使用递归查询
* @return MinioItem 列表
*/
public List<Item> getObjectsByPrefix(String bucketName,
String prefix,
boolean recursive) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
List<Item> list = new ArrayList<>();
Iterable<Result<Item>> objectsIterator = myMinioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());
if (objectsIterator != null) {
for (Result<Item> o : objectsIterator) {
Item item = o.get();
list.add(item);
}
}
return list;
}
/**
* 获取文件流
* @param bucketName 存储桶
* @param objectName 文件名
* @return 二进制流
*/
public InputStream getObject(String bucketName, String objectName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return myMinioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
}
/**
* 获取文件外链,有时间限制
* @param bucketName 存储桶
* @param objectName 文件名
* @param expires 外链有效时间(单位:秒)
* @return url
*/
@SneakyThrows(Exception.class)
public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) {
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().expiry(expires).bucket(bucketName).object(objectName).build();
return myMinioClient.getPresignedObjectUrl(args);
}
/**
* 获得文件外链,长期有效
* @param bucketName
* @param objectName
* @return url
*/
@SneakyThrows(Exception.class)
public String getPresignedObjectUrl(String bucketName, String objectName) {
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
.bucket(bucketName)
.object(objectName)
.method(Method.GET).build();
return myMinioClient.getPresignedObjectUrl(args);
}
/**
* 获取预签名链接、过期时间一天
* @param bucketName 桶名
* @param objectName 文件名称
* @return
*/
@SneakyThrows
public String getPresignedObjectUrl(String bucketName, String objectName,Map<String, String> queryParams) {
return myMinioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(bucketName)
.object(objectName)
.expiry(60 * 60 * 24)
.extraQueryParams(queryParams)
.build());
}
/****************************** 操作文件 ******************************/
/**
* 拷贝文件
* @param bucketName 存储桶
* @param objectName 文件名
* @param srcBucketName 目标存储桶
* @param srcObjectName 目标文件名
*/
@SneakyThrows(Exception.class)
public ObjectWriteResponse copyFile(String bucketName, String objectName, String srcBucketName, String srcObjectName) {
return myMinioClient.copyObject(
CopyObjectArgs.builder()
.source(CopySource.builder().bucket(bucketName).object(objectName).build())
.bucket(srcBucketName)
.object(srcObjectName)
.build());
}
/**
* 删除文件
* @param bucketName 存储桶
* @param objectName 文件名称
*/
@SneakyThrows(Exception.class)
public void removeFile(String bucketName, String objectName) {
myMinioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
}
/**
* 批量删除文件
* @param bucketName 存储桶
* @param keys 需要删除的文件列表
* @return
*/
public void removeFiles(String bucketName, List<String> keys) {
List<DeleteObject> objects = new LinkedList<>();
keys.forEach(s -> {
objects.add(new DeleteObject(s));
try {
removeFile(bucketName, s);
} catch (Exception e) {
log.error("[Minio工具类]>>>> 批量删除文件,异常:", e);
}
});
}
/****************************** 下载文件 ******************************/
/**
* 断点下载
* @param bucketName 存储桶
* @param objectName 文件名称
* @param offset 起始字节的位置
* @param length 要读取的长度
* @return 二进制流
*/
public InputStream getObject(String bucketName, String objectName, long offset, long length) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return myMinioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.offset(offset)
.length(length)
.build());
}
/****************************** 上传文件 ******************************/
/**
* 通过 MultipartFile 进行文件上传
* @param bucketName 存储桶
* @param file 文件名
* @param objectName 对象名
* @param contentType 文件类型
* @return
*/
public ObjectWriteResponse uploadFile(String bucketName, MultipartFile file, String objectName, String contentType) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
//先判断对象桶是否存在
boolean b = bucketExists(bucketName);
if(!b){
//若对象桶不存在,则创建
makeBucket(bucketName);
}
//把文件转化为输入流
InputStream inputStream = file.getInputStream();
//上传文件
ObjectWriteResponse objectWriteResponse = myMinioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.contentType(contentType)
.stream(inputStream, inputStream.available(), -1)
.build());
//上传文件后,关闭流
if(inputStream!=null){
inputStream.close();
}
return objectWriteResponse;
}
/**
* 通过文件完整路径 上传文件
* @param bucketName 存储桶
* @param objectName 对象名称
* @param filePath 本地文件路径
* @return
*/
public ObjectWriteResponse uploadFile(String bucketName, String objectName, String filePath) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return myMinioClient.uploadObject(
UploadObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.filename(filePath)
.build());
}
/**
* 通过输入流 上传文件
* @param bucketName 存储桶
* @param objectName 文件对象
* @param inputStream 文件流
* @return
*/
public ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return myMinioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(inputStream, inputStream.available(), -1)
.build());
}
/**
* 创建分片上传
*/
public CreateMultipartUploadResponse createMultipartUpload(String bucketName, String region, String objectName, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {
return myMinioClient.createMultipartUpload(bucketName, region, objectName, headers, extraQueryParams);
}
/**
* 完成分片上传(分片文件合并)
*/
public ObjectWriteResponse completeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {
return myMinioClient.completeMultipartUpload(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);
}
/**
* 查询分片文件
*/
public ListPartsResponse listParts(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 myMinioClient.listParts(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);
}
}
文件上传
前端代码
html
<!--element-plus的文件上传组件-->
<el-upload
ref="uploadRef"
action="#"
:limit="1"
:file-list="fileList"
:on-change="onChange"
:auto-upload="false"
>
<template #trigger>
<el-button type="primary">选取文件</el-button>
</template>
<el-button style="margin-left: 10px" type="success" @click="uploadlFile">
上传到服务器
</el-button>
</el-upload>
<el-progress
v-if="isShowProgress"
:text-inside="true"
:stroke-width="20"
:percentage="progressPercent"
/>
<script setup>
import axios from "axios"; //引入axios
import { ElMessage } from "element-plus";
let uploadRef = ref(undefined);
let isShowProgress = ref(false); //是否显示进度条
let progressPercent = ref(0); //进度条进度
let fileList = ref([]); //文件列表
//文件改变时,将新添加的文件加入到文件列表中
function onChange(file) {
fileList.value.push(file);
}
//自定义文件上传请求
function uploadlFile() {
//判断文件列表中是否有文件再上传
if (fileList.value.length === 0) {
return ElMessage.error("请选择要上传的文件");
}
//遍历文件列表,把列表中的每个文件都上传
fileList.value.forEach((file) => {
let param = new FormData();
param.append("file", file.raw);
param.append("bucketName", "media-episodes-bucket");
isShowProgress.value = true;
//调接口
axios
.request({
url: "/shuyx-minio/oss/upload",
method: "POST",
data: param,
headers: { "Content-Type": "multipart/form-data" },
//获取进度方法
onUploadProgress: (e) => {
progressPercent.value = Number(
((e.loaded / e.total) * 100).toFixed(0)
);
},
})
.then((res) => {
//设置文件列表中的文件的状态,变为成功
file.url = res.data.fileName;
file.status = "success";
//隐藏进度条,并把进度清空
isShowProgress.value = false;
progressPercent.value = 0;
});
});
}
</script>
后端代码
java
@RestController
@RequestMapping("/shuyx-minio/oss")
public class OSSController {
@Autowired
private MinioUtils minioUtils;
//用户头像文件对象桶
private String USER_AVATAR_BUCKET = "user-avatar-bucket";
//媒体剧集文件对象桶
private String MEDIA_EPISODES_BUCKET = "media-episodes-bucket";
/**
* 上传文件接口
* @param file
*/
@PostMapping("/upload")
public Object upload(@RequestParam("file") MultipartFile file,String bucketName) {
log.info("/shuyx-minio/oss/upload, file,{}",file);
double size = (double) (file.getSize() / 1024 / 1024) + 1;
log.info(file.getOriginalFilename() +" 文件大小为 "+size+" MB之内。");
try {
//新文件名
String newFileName = System.currentTimeMillis() + "." + file.getOriginalFilename();
//文件类型
String contentType = file.getContentType();
//上传文件
minioUtils.uploadFile(bucketName, file, newFileName, contentType);
JSONObject jsonObject = new JSONObject();
jsonObject.put("fileName",newFileName);
return ReturnUtil.success(jsonObject);
} catch (Exception e) {
log.error("上传文件失败。请查询日志。");
e.printStackTrace();
return ReturnUtil.fail(ResultCodeEnum.HTTP_REQUEST_ERROR);
}
}
}
分片文件上传
当要上传的文件太大的时候。我们可以在前端,将大文件进行分片。然后把多个分片文件都上传到 minio 中。然后再 minio 中将分片文件拼接成大文件。
假设一个大文件,大小为 444M。并且设置每个分片文件的大小为 100M。那么这个大文件会被分成 5 个分片文件。
分片文件上传逻辑:
- 前端通过后端向 minio 发送分片上传请求。参数是文件名称,文件分片数量,对象桶名称
- minio 会根据这些参数,创建一个 uploadId。
- 我们拿着 uploadId,再向 minio 获取预签名链接。链接个数与分片个数相同。
- 最后把 uploadId 和多个预签名链接。作为响应结果返回。
- 前端将大文件进行分片。每个分片对应一个预签名链接。
- 然后前端直接向 minio 发送 put 请求。参数就是分片文件和对应的预签名链接。
- 当所有分片文件都上传到 minio 的时候。前端通过后端向 minio 发送分片文件合并请求。把 minio 中的分片文件合并为一个大文件。
注意:当我们拿到预签名链接后,前端是直接向minio服务端发送put请求,来上传分片文件。此时的put请求是没有经过后端的。
前端代码:
html
<!--element-plus的文件上传组件-->
<el-upload
ref="uploadRef"
action="#"
:limit="1"
:file-list="fileList"
:on-change="onChange"
:auto-upload="false"
>
<template #trigger>
<el-button type="primary">选取文件</el-button>
</template>
<el-button style="margin-left: 10px" type="success" @click="uploadlPartFile">
上传到服务器
</el-button>
</el-upload>
<el-progress
v-if="isShowProgress"
:text-inside="true"
:stroke-width="20"
:percentage="progressPercent"
/>
<script setup>
import axios from "axios"; //引入axios
import { ElMessage } from "element-plus";
//自定义分片文件上传请求
function uploadlPartFile() {
//判断文件列表中是否有文件再上传
if (fileList.value.length === 0) {
return ElMessage.error("请选择要上传的文件");
}
//获取文件列表中的第一个文件
let currentFile = fileList.value[0].raw;
console.log("11", currentFile);
//将文件分片,得到分片数组
let fileChunkList = createFileChunk(currentFile);
//请求参数
let param = new FormData();
param.append("fileName", currentFile.name);
param.append("bucketName", "media-episodes-bucket");
param.append("chunkNum", fileChunkList.length);
//创建分片上传请求
axios.request({
url: "/shuyx-minio/oss/createMultipartUpload",
method: "POST",
data: param,
headers: { "Content-Type": "multipart/form-data" },
})
.then(async (res) => {
//uploadId
let uploadId = res.data.uploadId;
//预签名链接数组
let chunkList = res.data.uploadUrlList;
//依次上传分片文件
for (let i = 0; i < fileChunkList.length; i++) {
console.log("------------------------------------");
console.log("开始上传第" + (i + 1) + "个分片");
//此处用axios发送put请求,该请求用于上传分片文件
//请求参数就是分片文件和对应的预签名链接
//注意该请求是同步的。即一个分片文件上传完毕之后,再上传下一个分片文件
await axios({
method: "put",
url: chunkList[i],
data: fileChunkList[i].file,
}).then(async function (res) {
if (res.status == 200) {
console.log("第" + (i + 1) + "个分片,上传成功");
} else {
console.log("第" + (i + 1) + "个分片,上传失败");
}
});
}
//如果最后一个分片文件上传完毕,那么就调用分片文件合并请求
let obj = {
bucketName: "media-episodes-bucket",
fileName: currentFile.name,
uploadId: uploadId,
};
console.log("开始合并分片");
axios.request({
url: "/shuyx-minio/oss/mergePartFile",
method: "POST",
data: obj,
headers: { "Content-Type": "multipart/form-data" },
})
.then((r) => {
console.log("mergePartFile", r);
console.log("分片合并完成");
});
});
}
/**
* 把文件分片
*/
function createFileChunk(file) {
let fileChunkList = [];
let chunkSize = 100 * 1024 * 1024; //每个分片大小为100mb
let count = 0;
while (count < file.size) {
fileChunkList.push({
file: file.slice(count, count + chunkSize),
});
count += chunkSize;
}
return fileChunkList;
}
</script>
后端代码
java
@RestController
@RequestMapping("/shuyx-minio/oss")
public class OSSController {
@Autowired
private MinioUtils minioUtils;
//用户头像文件对象桶
private String USER_AVATAR_BUCKET = "user-avatar-bucket";
//媒体剧集文件对象桶
private String MEDIA_EPISODES_BUCKET = "media-episodes-bucket";
/**
* 创建分片文件请求等信息
* 逻辑:
* 1. minio接收到前端发来的创建分片文件请求。会返回对应的uploadId
* 2. 然后通过uploadId以及分片数量,创建同等数量的预签名链接。并返回
* @return
*/
@PostMapping("/createMultipartUpload")
public Object createMultipartUpload(String fileName,String bucketName,Integer chunkNum) {
log.info("/shuyx-minio/oss/createMultipartUpload, bucketName {} , fileName {},chunkNum {}",bucketName,fileName,chunkNum);
log.info("文件名称为"+fileName);
log.info("对象桶名称为"+bucketName);
log.info("分片数量为"+chunkNum);
// 1. 向minio创建分片上传请求,会返回一个uploadId
CreateMultipartUploadResponse response = minioUtils.createMultipartUpload(bucketName, null, fileName, null, null);
// 2. 获取uploadId
String uploadId = response.result().uploadId();
// 3. 返回结果信息
Map<String, Object> result = new HashMap<>();
result.put("uploadId",uploadId);
ArrayList<String> uploadUrlList = new ArrayList<>();
// 4. 请求Minio,获取每个分块,对应的签名上传URL
Map<String, String> partMap = new HashMap<>();
partMap.put("uploadId", uploadId);
// 5. 根据分片数量,循环获取各个分片的预签名链接
for (int i = 1; i <= chunkNum; i++) {
partMap.put("partNumber", String.valueOf(i));
String uploadUrl = minioUtils.getPresignedObjectUrl(bucketName, fileName, partMap);//获取每个分片文件的预签名链接
uploadUrlList.add(uploadUrl); //添加预签名链接
}
result.put("uploadUrlList",uploadUrlList);
//返回
return ReturnUtil.success(result);
}
/**
* 合并分片文件
*/
@PostMapping("/mergePartFile")
public Object mergePartFile(String bucketName,String fileName,String uploadId) throws ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, IOException, InvalidKeyException, XmlParserException, InvalidResponseException, InternalException {
//先查询minio中具有相同uploadId的分片文件
ListPartsResponse listPartsResponse = minioUtils.listParts(bucketName,null,
fileName,null,null, uploadId,null,null);
List<Part> parts = listPartsResponse.result().partList();
//找到这些分片文件之后,开始合并分片文件
minioUtils.completeMultipartUpload(bucketName, null,
fileName, uploadId, parts.toArray(new Part[]{}), null, null);
return ReturnUtil.success();
}
}