|
@@ -0,0 +1,342 @@
|
|
|
+package com.persagy.dmp.storage.service.impl;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.persagy.dmp.storage.service.MinioCommonService;
|
|
|
+import io.minio.*;
|
|
|
+import io.minio.http.Method;
|
|
|
+import io.minio.messages.Item;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.SneakyThrows;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.InitializingBean;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class MinioCommonServiceImpl implements MinioCommonService, InitializingBean {
|
|
|
+
|
|
|
+ private final MinioClient minioClient;
|
|
|
+
|
|
|
+ @Value("${persagy.common.file.minio.bucket.chunk:persagy-bdtp-chuck}")
|
|
|
+ private String chunkBucKet;
|
|
|
+
|
|
|
+ /**排序*/
|
|
|
+ public final static boolean SORT = true;
|
|
|
+ /**不排序*/
|
|
|
+ public final static boolean NOT_SORT = false;
|
|
|
+ /**默认过期时间(分钟)*/
|
|
|
+ private final static Integer DEFAULT_EXPIRY = 60;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void afterPropertiesSet() throws Exception {
|
|
|
+ //方便管理分片文件,则单独创建一个分片文件的存储桶
|
|
|
+ if (!isBucketExist(chunkBucKet)){
|
|
|
+ createBucket(chunkBucKet);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 存储桶是否存在
|
|
|
+ * @param bucketName: 存储桶名称
|
|
|
+ * @return true/false
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:35
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @SneakyThrows
|
|
|
+ public boolean isBucketExist(String bucketName){
|
|
|
+ return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 创建存储桶
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @return : true/false
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:35
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @SneakyThrows
|
|
|
+ public boolean createBucket(String bucketName){
|
|
|
+ minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 获取访问对象的外链地址
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param objectName : 对象名称
|
|
|
+ * @param expiry : 过期时间(分钟) 最大为7天 超过7天则默认最大值
|
|
|
+ * @return : viewUrl
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:41
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @SneakyThrows
|
|
|
+ public String getObjectUrl(String bucketName, String objectName, Integer expiry){
|
|
|
+ expiry = expiryHandle(expiry);
|
|
|
+ return minioClient.getPresignedObjectUrl(
|
|
|
+ GetPresignedObjectUrlArgs.builder()
|
|
|
+ .method(Method.GET)
|
|
|
+ .bucket(bucketName)
|
|
|
+ .object(objectName)
|
|
|
+ .expiry(expiry)
|
|
|
+ .build()
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 创建上传文件对象的外链
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param objectName : 欲上传文件对象的名称
|
|
|
+ * @param expiry : 过期时间(分钟) 最大为7天 超过7天则默认最大值
|
|
|
+ * @return : uploadUrl
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:41
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @SneakyThrows
|
|
|
+ public String createUploadUrl(String bucketName, String objectName, Integer expiry){
|
|
|
+ expiry = expiryHandle(expiry);
|
|
|
+ return minioClient.getPresignedObjectUrl(
|
|
|
+ GetPresignedObjectUrlArgs.builder()
|
|
|
+ .method(Method.PUT)
|
|
|
+ .bucket(bucketName)
|
|
|
+ .object(objectName)
|
|
|
+ .expiry(expiry)
|
|
|
+ .build()
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 创建上传文件对象的外链
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param objectName : 欲上传文件对象的名称
|
|
|
+ * @return : java.lang.String
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:42
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String createUploadUrl(String bucketName, String objectName){
|
|
|
+ return createUploadUrl(bucketName,objectName,DEFAULT_EXPIRY);
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 批量创建分片上传外链
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param objectMD5 : 欲上传分片文件主文件的MD5
|
|
|
+ * @param chunkCount : 分片数量
|
|
|
+ * @return : uploadChunkUrls
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:43
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<String> createUploadChunkUrlList(String bucketName, String objectMD5, Integer chunkCount){
|
|
|
+ if (StrUtil.isBlank(bucketName)){
|
|
|
+ bucketName = chunkBucKet;
|
|
|
+ }
|
|
|
+ if (StrUtil.isBlank(objectMD5)){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ objectMD5 += "/";
|
|
|
+ if(null == chunkCount || 0 == chunkCount){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ List<String> urlList = new ArrayList<>(chunkCount);
|
|
|
+ for (int i = 1; i <= chunkCount; i++){
|
|
|
+ String objectName = objectMD5 + i + ".chunk";
|
|
|
+ urlList.add(createUploadUrl(bucketName,objectName,DEFAULT_EXPIRY));
|
|
|
+ }
|
|
|
+ return urlList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 创建指定序号的分片文件上传外链
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param objectMD5 : 欲上传分片文件主文件的MD5
|
|
|
+ * @param partNumber : 分片序号
|
|
|
+ * @return : uploadChunkUrl
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:44
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String createUploadChunkUrl(String bucketName, String objectMD5, Integer partNumber){
|
|
|
+ if (StrUtil.isBlank(bucketName)){
|
|
|
+ bucketName = chunkBucKet;
|
|
|
+ }
|
|
|
+ if (StrUtil.isBlank(objectMD5)){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ objectMD5 += "/" + partNumber + ".chunk";
|
|
|
+ return createUploadUrl(bucketName,objectMD5,DEFAULT_EXPIRY);
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 获取对象文件名称列表
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param prefix : 对象名称前缀
|
|
|
+ * @param sort : 是否排序(升序)
|
|
|
+ * @return : objectNames
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:45
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @SneakyThrows
|
|
|
+ public List<String> listObjectNames(String bucketName, String prefix, Boolean sort){
|
|
|
+ ListObjectsArgs listObjectsArgs;
|
|
|
+ if(StrUtil.isBlank(prefix)){
|
|
|
+ listObjectsArgs = ListObjectsArgs.builder()
|
|
|
+ .bucket(bucketName)
|
|
|
+ .recursive(true)
|
|
|
+ .build();
|
|
|
+ }else {
|
|
|
+ listObjectsArgs = ListObjectsArgs.builder()
|
|
|
+ .bucket(bucketName)
|
|
|
+ .prefix(prefix)
|
|
|
+ .recursive(true)
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+ Iterable<Result<Item>> chunks = minioClient.listObjects(listObjectsArgs);
|
|
|
+ List<String> chunkPaths = new ArrayList<>();
|
|
|
+ for (Result<Item> item : chunks){
|
|
|
+ chunkPaths.add(item.get().objectName());
|
|
|
+ }
|
|
|
+ if (sort){
|
|
|
+ return chunkPaths.stream().distinct().collect(Collectors.toList());
|
|
|
+ }
|
|
|
+ return chunkPaths;
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 获取对象文件名称列表
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param prefix : 对象名称前缀
|
|
|
+ * @return : objectNames
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:47
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<String> listObjectNames(String bucketName, String prefix){
|
|
|
+ return listObjectNames(bucketName, prefix, NOT_SORT);
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 获取分片文件名称列表
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param objectMd5 : 对象Md5
|
|
|
+ * @return : objectChunkNames
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:48
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<String> listChunkObjectNames(String bucketName, String objectMd5){
|
|
|
+ if (StrUtil.isBlank(bucketName)){
|
|
|
+ bucketName = chunkBucKet;
|
|
|
+ }
|
|
|
+ if (StrUtil.isBlank(objectMd5)){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return listObjectNames(bucketName,objectMd5,SORT);
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 获取分片名称地址HashMap key=分片序号 value=分片文件地址
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param objectMd5 : 对象Md5
|
|
|
+ * @return : objectChunkNameMap
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:49
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Map<Integer, String> mapChunkObjectNames(String bucketName, String objectMd5){
|
|
|
+ if (StrUtil.isBlank(bucketName)){
|
|
|
+ bucketName = chunkBucKet;
|
|
|
+ }
|
|
|
+ if (StrUtil.isBlank(objectMd5)){
|
|
|
+ return new HashMap<>();
|
|
|
+ }
|
|
|
+ List<String> chunkPaths = listObjectNames(bucketName,objectMd5);
|
|
|
+ if (CollUtil.isEmpty(chunkPaths)){
|
|
|
+ return new HashMap<>();
|
|
|
+ }
|
|
|
+ Map<Integer, String> chunkMap = new HashMap<>(chunkPaths.size());
|
|
|
+ for (String chunkName : chunkPaths) {
|
|
|
+ Integer partNumber = Integer.parseInt(chunkName.substring(chunkName.indexOf("/") + 1,chunkName.lastIndexOf(".")));
|
|
|
+ chunkMap.put(partNumber,chunkName);
|
|
|
+ }
|
|
|
+ return chunkMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 合并分片文件成对象文件
|
|
|
+ * @param chunkBucKetName : 分片文件所在存储桶名称
|
|
|
+ * @param composeBucketName : 合并后的对象文件存储的存储桶名称
|
|
|
+ * @param chunkNames : 分片文件名称集合
|
|
|
+ * @param objectName : 合并后的对象文件名称
|
|
|
+ * @return : boolean
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:50
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @SneakyThrows
|
|
|
+ public boolean composeObject(String chunkBucKetName, String composeBucketName, List<String> chunkNames, String objectName){
|
|
|
+ if (StrUtil.isBlank(chunkBucKetName)){
|
|
|
+ chunkBucKetName = chunkBucKet;
|
|
|
+ }
|
|
|
+ List<ComposeSource> sourceObjectList = new ArrayList<>(chunkNames.size());
|
|
|
+ for (String chunk : chunkNames){
|
|
|
+ sourceObjectList.add(
|
|
|
+ ComposeSource.builder()
|
|
|
+ .bucket(chunkBucKetName)
|
|
|
+ .object(chunk)
|
|
|
+ .build()
|
|
|
+ );
|
|
|
+ }
|
|
|
+ minioClient.composeObject(
|
|
|
+ ComposeObjectArgs.builder()
|
|
|
+ .bucket(composeBucketName)
|
|
|
+ .object(objectName)
|
|
|
+ .sources(sourceObjectList)
|
|
|
+ .build()
|
|
|
+ );
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 合并分片文件成对象文件
|
|
|
+ * @param bucketName : 存储桶名称
|
|
|
+ * @param chunkNames : 分片文件名称集合
|
|
|
+ * @param objectName : 合并后的对象文件名称
|
|
|
+ * @return : true/false
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:51
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public boolean composeObject(String bucketName, List<String> chunkNames, String objectName){
|
|
|
+ return composeObject(chunkBucKet,bucketName,chunkNames,objectName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /***
|
|
|
+ * Description: 将分钟数转换为秒数
|
|
|
+ * @param expiry : 过期时间(分钟数)
|
|
|
+ * @return : int
|
|
|
+ * @author : lijie
|
|
|
+ * Update By lijie 2021/10/16 14:51
|
|
|
+ */
|
|
|
+ private static int expiryHandle(Integer expiry){
|
|
|
+ expiry = expiry * 60;
|
|
|
+ if (expiry > 604800){
|
|
|
+ return 604800;
|
|
|
+ }
|
|
|
+ return expiry;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|