From 10e746abd43afda1a36f63208771a0aee627166e Mon Sep 17 00:00:00 2001 From: xudawei Date: Mon, 10 Mar 2025 19:55:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:(REQ-3540)=20=E6=96=87=E4=BB=B6=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ServerFileController.java | 26 +++-- .../oss/http/api/ServerFileServiceApi.java | 8 ++ .../ServerFileBatchCopyObjectRequest.java | 79 +++++++++++++ .../ServerFileBatchCopyObjectResponse.java | 53 +++++++++ .../oss/integration/s3/AliOssService.java | 6 + .../integration/s3/HuaWeiCloudService.java | 5 +- .../s3/impl/AliOssServiceImpl.java | 14 +++ .../s3/impl/HuaWeiCloudServiceImpl.java | 16 +-- .../cn/axzo/oss/manager/api/FileManager.java | 11 ++ .../api/dto/request/CopyObjectCloudDto.java | 84 ++++++++++++++ .../api/dto/request/FileCopyObjectDto.java | 63 ++++++++++ .../dto/response/FileCopyObjectResponse.java | 71 ++++++++++++ .../oss/manager/impl/FileManagerImpl.java | 36 +++++- .../cn/axzo/oss/service/api/FileService.java | 9 ++ .../oss/service/impl/FileServiceImpl.java | 109 +++++++++++++++++- 15 files changed, 565 insertions(+), 25 deletions(-) create mode 100644 oss-http-api/src/main/java/cn/axzo/oss/http/model/copyobject/ServerFileBatchCopyObjectRequest.java create mode 100644 oss-http-api/src/main/java/cn/axzo/oss/http/model/copyobject/ServerFileBatchCopyObjectResponse.java create mode 100644 oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/CopyObjectCloudDto.java create mode 100644 oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/FileCopyObjectDto.java create mode 100644 oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/response/FileCopyObjectResponse.java diff --git a/oss-client/src/main/java/cn/axzo/oss/client/controller/ServerFileController.java b/oss-client/src/main/java/cn/axzo/oss/client/controller/ServerFileController.java index fd63d7c..bc4a34e 100644 --- a/oss-client/src/main/java/cn/axzo/oss/client/controller/ServerFileController.java +++ b/oss-client/src/main/java/cn/axzo/oss/client/controller/ServerFileController.java @@ -1,12 +1,9 @@ package cn.axzo.oss.client.controller; +import cn.axzo.basics.common.util.AssertUtil; import cn.axzo.core.utils.converter.BeanConverter; import cn.axzo.framework.auth.domain.ContextInfo; -import cn.axzo.oss.common.enums.ChannelTypeEnum; -import cn.axzo.oss.common.enums.CodeEnum; -import cn.axzo.oss.common.exception.BizException; import cn.axzo.oss.common.utils.BeanConvertUtil; -import cn.axzo.oss.common.utils.UrlUtil; import cn.axzo.oss.http.api.ServerFileServiceApi; import cn.axzo.oss.http.model.ApiSignUrlDownloadRequest; import cn.axzo.oss.http.model.ApiSignUrlDownloadResponse; @@ -30,27 +27,28 @@ import cn.axzo.oss.http.model.ServerFileUploadByUrlRequest; import cn.axzo.oss.http.model.ServerFileUploadRequest; import cn.axzo.oss.http.model.ServerFileUploadResponse; import cn.axzo.oss.http.model.ServerFileUploadV2Request; +import cn.axzo.oss.http.model.copyobject.ServerFileBatchCopyObjectRequest; +import cn.axzo.oss.http.model.copyobject.ServerFileBatchCopyObjectResponse; +import cn.axzo.oss.manager.api.dto.request.FileCopyObjectDto; import cn.axzo.oss.manager.api.dto.request.FindFileKeyDto; import cn.axzo.oss.manager.api.dto.request.FindFileUrlDto; -import cn.axzo.oss.manager.api.dto.request.ServerFileCopyToDictDto; import cn.axzo.oss.manager.api.dto.request.GetObjectMetaDto; +import cn.axzo.oss.manager.api.dto.request.ServerFileCopyToDictDto; import cn.axzo.oss.manager.api.dto.request.ServerFileDeleteDto; import cn.axzo.oss.manager.api.dto.request.ServerFileUploadByUrlDto; import cn.axzo.oss.manager.api.dto.request.ServerFileUploadDto; import cn.axzo.oss.manager.api.dto.request.SignUrlDownloadDto; import cn.axzo.oss.manager.api.dto.request.SignUrlUploadDto; -import cn.axzo.oss.manager.api.dto.response.ManaGetObjectMetaResponse; +import cn.axzo.oss.manager.api.dto.response.FileCopyObjectResponse; import cn.axzo.oss.service.api.FileByUrlService; import cn.axzo.oss.service.api.FileCopyToDictService; import cn.axzo.oss.service.api.FileService; import cn.azxo.framework.common.model.CommonResponse; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.json.JSONUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @@ -231,4 +229,14 @@ public class ServerFileController implements ServerFileServiceApi { dto.setBizScene(dto.getAppCode() + "-public"); return CommonResponse.success(BeanConverter.convert(fileService.signUrlUpload(dto, ContextInfo.LiteSaasContext.builder().build()), FetchUploadSignUrlToPublishResponse.class)); } + + /** + * 复制文件 + */ + public CommonResponse batchCopyObject(ServerFileBatchCopyObjectRequest request) { + AssertUtil.isFalse(Objects.isNull(request) || CollectionUtils.isEmpty(request.getCopyObjects()), "入参为空"); + List fileCopyObjectDtos = BeanUtil.copyToList(request.getCopyObjects(), FileCopyObjectDto.class); + List fileCopyObjectResponses = fileService.batchCopyObject(fileCopyObjectDtos, request.getAppCode()); + return CommonResponse.success(ServerFileBatchCopyObjectResponse.builder().responses(BeanUtil.copyToList(fileCopyObjectResponses, ServerFileBatchCopyObjectResponse.ServerFileCopyObjectResponse.class)).build()); + } } diff --git a/oss-http-api/src/main/java/cn/axzo/oss/http/api/ServerFileServiceApi.java b/oss-http-api/src/main/java/cn/axzo/oss/http/api/ServerFileServiceApi.java index d2ae113..09ac9b4 100644 --- a/oss-http-api/src/main/java/cn/axzo/oss/http/api/ServerFileServiceApi.java +++ b/oss-http-api/src/main/java/cn/axzo/oss/http/api/ServerFileServiceApi.java @@ -22,6 +22,8 @@ import cn.axzo.oss.http.model.ServerFileUploadByUrlRequest; import cn.axzo.oss.http.model.ServerFileUploadRequest; import cn.axzo.oss.http.model.ServerFileUploadResponse; import cn.axzo.oss.http.model.ServerFileUploadV2Request; +import cn.axzo.oss.http.model.copyobject.ServerFileBatchCopyObjectRequest; +import cn.axzo.oss.http.model.copyobject.ServerFileBatchCopyObjectResponse; import cn.azxo.framework.common.model.CommonResponse; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.RequestBody; @@ -135,4 +137,10 @@ public interface ServerFileServiceApi { @RequestMapping(value = "/api/server/copyToDict", method = RequestMethod.POST) CommonResponse copyToDict(ServerFileCopyToDictRequest request); + /** + * 复制文件 + */ + @RequestMapping(value = "/api/server/batchCopyObject", method = RequestMethod.POST) + CommonResponse batchCopyObject(ServerFileBatchCopyObjectRequest request); + } diff --git a/oss-http-api/src/main/java/cn/axzo/oss/http/model/copyobject/ServerFileBatchCopyObjectRequest.java b/oss-http-api/src/main/java/cn/axzo/oss/http/model/copyobject/ServerFileBatchCopyObjectRequest.java new file mode 100644 index 0000000..61b6dce --- /dev/null +++ b/oss-http-api/src/main/java/cn/axzo/oss/http/model/copyobject/ServerFileBatchCopyObjectRequest.java @@ -0,0 +1,79 @@ +package cn.axzo.oss.http.model.copyobject; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.Set; + +/** + * 批量复制对象 + * + * @author xudawei + * @date 2025-03-07 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ServerFileBatchCopyObjectRequest { + + @NotBlank(message = "appCode must not be null") + private String appCode; + + @NotEmpty(message = "集合对象不能为空") + private Set copyObjects; + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class ServerFileCopyObjectRequest { + /** + * 唯一标识 + * 没有唯一标识,可以与fileKey一致 + */ + @NotBlank(message = "唯一性标识不能为空") + private String fileUuid; + + /** + * 目标桶 + * 如果目标桶为空,则默认取源桶 + */ + private String targetBucketName; + + /** + * 目标目录 + * 如果目标目录为空,则默认在源目录同级 + */ + private String targetDict; + + /** + * 目标通道 + * 如果目标通道为空,则默认源通道 + */ + private String targetChannelCode; + + /** + * 文件名称 + */ + private String targetFileName; + + /** + * 桶名称 + */ + private String srcBucketName; + /** + * 桶文件目录 + */ + private String srcBucketKey; + /** + * 文件对象key + */ + private String srcFileKey; + + } +} diff --git a/oss-http-api/src/main/java/cn/axzo/oss/http/model/copyobject/ServerFileBatchCopyObjectResponse.java b/oss-http-api/src/main/java/cn/axzo/oss/http/model/copyobject/ServerFileBatchCopyObjectResponse.java new file mode 100644 index 0000000..e86ff71 --- /dev/null +++ b/oss-http-api/src/main/java/cn/axzo/oss/http/model/copyobject/ServerFileBatchCopyObjectResponse.java @@ -0,0 +1,53 @@ +package cn.axzo.oss.http.model.copyobject; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 复制至指定目录下 + * + * @author xudawei + * @date 2024-07-01 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ServerFileBatchCopyObjectResponse { + + private List responses; + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class ServerFileCopyObjectResponse { + /** + * 与入参中的id一致 + */ + private String id; + + /** + * 原文件的fileKey + */ + private String oldFileKey; + + /** + * 原文件的url + */ + private String oldFileUrl; + + /** + * 新文件的filekey + */ + private String newFileKey; + + private String newUrl; + + } + +} diff --git a/oss-integration/src/main/java/cn/axzo/oss/integration/s3/AliOssService.java b/oss-integration/src/main/java/cn/axzo/oss/integration/s3/AliOssService.java index 2d9dd56..ad323db 100644 --- a/oss-integration/src/main/java/cn/axzo/oss/integration/s3/AliOssService.java +++ b/oss-integration/src/main/java/cn/axzo/oss/integration/s3/AliOssService.java @@ -1,6 +1,7 @@ package cn.axzo.oss.integration.s3; import cn.axzo.oss.integration.s3.base.BaseS3Service; +import com.aliyun.oss.model.CopyObjectResult; import com.aliyun.oss.model.SimplifiedObjectMeta; /** @@ -40,4 +41,9 @@ public interface AliOssService extends BaseS3Service { * 通过url上传至OBS */ String uploadByUrl(String bucketName, String tgtFileKey, String fileName, String url); + + /** + * 复制对象 + */ + CopyObjectResult copyObject(String bucketName, String key, String targetBucketName, String targetKey); } diff --git a/oss-integration/src/main/java/cn/axzo/oss/integration/s3/HuaWeiCloudService.java b/oss-integration/src/main/java/cn/axzo/oss/integration/s3/HuaWeiCloudService.java index efe3385..8f9ea90 100644 --- a/oss-integration/src/main/java/cn/axzo/oss/integration/s3/HuaWeiCloudService.java +++ b/oss-integration/src/main/java/cn/axzo/oss/integration/s3/HuaWeiCloudService.java @@ -1,5 +1,6 @@ package cn.axzo.oss.integration.s3; +import com.obs.services.model.CopyObjectResult; import com.obs.services.model.ObjectMetadata; import com.obs.services.model.TemporarySignatureResponse; @@ -53,7 +54,7 @@ public interface HuaWeiCloudService { ObjectMetadata getObjectMeta(String bucketName, String key, String url); /** - * 元数据 + * 复制对象 */ - boolean copyToDict(String bucketName, String key, String targetBucketName, String targetKey); + CopyObjectResult copyObject(String bucketName, String key, String targetBucketName, String targetKey); } diff --git a/oss-integration/src/main/java/cn/axzo/oss/integration/s3/impl/AliOssServiceImpl.java b/oss-integration/src/main/java/cn/axzo/oss/integration/s3/impl/AliOssServiceImpl.java index e33c963..08b74a9 100644 --- a/oss-integration/src/main/java/cn/axzo/oss/integration/s3/impl/AliOssServiceImpl.java +++ b/oss-integration/src/main/java/cn/axzo/oss/integration/s3/impl/AliOssServiceImpl.java @@ -11,6 +11,7 @@ import com.aliyun.oss.HttpMethod; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSException; import com.aliyun.oss.model.CompleteMultipartUploadRequest; +import com.aliyun.oss.model.CopyObjectResult; import com.aliyun.oss.model.GeneratePresignedUrlRequest; import com.aliyun.oss.model.GetObjectRequest; import com.aliyun.oss.model.InitiateMultipartUploadRequest; @@ -415,4 +416,17 @@ public class AliOssServiceImpl implements AliOssService { return getUrl(bucketName, tgtFileKey); } + + /** + * 复制对象 + */ + @Override + public CopyObjectResult copyObject(String bucketName, String key, String targetBucketName, String targetKey) { + try { + return aliOssClient.getClient().copyObject(bucketName, key, targetBucketName, targetKey); + } catch (Exception e) { + log.warn("aliyun-copyObject-exception, bucketName:{}, key:{}", bucketName, key, e); + return null; + } + } } diff --git a/oss-integration/src/main/java/cn/axzo/oss/integration/s3/impl/HuaWeiCloudServiceImpl.java b/oss-integration/src/main/java/cn/axzo/oss/integration/s3/impl/HuaWeiCloudServiceImpl.java index 2a5697e..6ffe292 100644 --- a/oss-integration/src/main/java/cn/axzo/oss/integration/s3/impl/HuaWeiCloudServiceImpl.java +++ b/oss-integration/src/main/java/cn/axzo/oss/integration/s3/impl/HuaWeiCloudServiceImpl.java @@ -15,7 +15,7 @@ import com.obs.services.ObsClient; import com.obs.services.exception.ObsException; import com.obs.services.model.CompleteMultipartUploadRequest; import com.obs.services.model.CompleteMultipartUploadResult; -import com.obs.services.model.CopyObjectRequest; +import com.obs.services.model.CopyObjectResult; import com.obs.services.model.DeleteObjectResult; import com.obs.services.model.DownloadFileRequest; import com.obs.services.model.DownloadFileResult; @@ -475,21 +475,15 @@ public class HuaWeiCloudServiceImpl implements HuaWeiCloudService { } /** - * 元数据 + * 复制对象 */ @Override - public boolean copyToDict(String bucketName, String key, String targetBucketName, String targetKey) { + public CopyObjectResult copyObject(String bucketName, String key, String targetBucketName, String targetKey) { try { - CopyObjectRequest request = new CopyObjectRequest(); - request.setSourceBucketName(bucketName); - request.setSourceObjectKey(key); - request.setDestinationBucketName(targetBucketName); - request.setDestinationObjectKey(targetKey); - huaWeiCloudObsClient.getClient().copyObject(request); - return true; + return huaWeiCloudObsClient.getClient().copyObject(bucketName, key, targetBucketName, targetKey); } catch (Exception e) { log.warn("huaweicloud-copyToDict-exception, bucketName:{}, key:{}", bucketName, key, e); - return false; + return null; } } diff --git a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/FileManager.java b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/FileManager.java index 1f4cb88..a53a04e 100644 --- a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/FileManager.java +++ b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/FileManager.java @@ -1,6 +1,7 @@ package cn.axzo.oss.manager.api; import cn.axzo.oss.manager.api.dto.PartETag; +import cn.axzo.oss.manager.api.dto.request.CopyObjectCloudDto; import cn.axzo.oss.manager.api.dto.request.MultipartUploadDto; import cn.axzo.oss.manager.api.dto.response.ManaGetObjectMetaResponse; import cn.axzo.oss.manager.api.dto.response.SignUrlDownloadResponse; @@ -95,4 +96,14 @@ public interface FileManager { * 构建公开Style */ String buildPublicXImageProcess(String url, String style); + + /** + * 复制对象 + */ + Boolean copyObjects(CopyObjectCloudDto dto); + + /** + * 相同的通道-对象复制 + */ + Boolean copyObjectWhenSameChannel(CopyObjectCloudDto dto); } diff --git a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/CopyObjectCloudDto.java b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/CopyObjectCloudDto.java new file mode 100644 index 0000000..f0cdd21 --- /dev/null +++ b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/CopyObjectCloudDto.java @@ -0,0 +1,84 @@ +package cn.axzo.oss.manager.api.dto.request; + +import cn.axzo.oss.common.utils.Utility; +import cn.axzo.oss.dal.entity.File; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +/** + * @author xudawei@axzo.cn + * @date 2025/3/10 + * @description 对象复制Dto + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CopyObjectCloudDto { + + /** + * 源桶名称 + * 比如:axzo-oss + */ + private String srcBucketName; + /** + * 源桶key(三方的桶上的目录+文件名称) + * 比如 zeus/recruit_collect/93248404217a4576ba2698183c3fc7a1.xlsx + */ + private String srcBucketKey; + /** + * 源通道 + */ + private String srcChannelCode; + /** + * 源Url + */ + private String srcUrl; + + /** + * 目的桶名称 + */ + private String targetBucketName; + /** + * 目的桶key(三方的桶上的目录+文件名称) + */ + private String targetBucketKey; + /** + * 目的key(文件唯一标识) + * 比如:93248404217a4576ba2698183c3fc7a1 + */ + private String targetKey; + private String targetChannelCode; + + private String fileName; + + private File file; + private String fileUuid; + + public static CopyObjectCloudDto create(FileCopyObjectDto fileCopyObjectRequest, File file) { + // 入参中有通道,则优先取入参;否则取源通道 + String targetChannelCode = StringUtils.isBlank(fileCopyObjectRequest.getTargetChannelCode()) ? file.getChannelCode() : fileCopyObjectRequest.getTargetChannelCode(); + String targetBucketName = StringUtils.isBlank(fileCopyObjectRequest.getTargetBucketName()) ? file.getBucketName() : fileCopyObjectRequest.getTargetBucketName(); + String targetBucketDict = StringUtils.isBlank(fileCopyObjectRequest.getTargetDict()) ? file.getDirectory() : fileCopyObjectRequest.getTargetDict(); + String fileName = StringUtils.isBlank(fileCopyObjectRequest.getTargetFileName()) ? file.getFileName() : fileCopyObjectRequest.getTargetFileName(); + + String uuid = Utility.getUUID(); + + return CopyObjectCloudDto.builder() + .srcBucketName(file.getBucketName()) + .srcBucketKey(Utility.generateFileKey(file.getDirectory(), file.getFileUuid(), file.getFileFormat())) + .srcChannelCode(file.getChannelCode()) + .targetBucketName(targetBucketName) + .targetBucketKey(Utility.generateFileKey(targetBucketDict, uuid, file.getFileFormat())) + .targetKey(uuid) + .targetChannelCode(targetChannelCode) + .fileName(fileName) + .file(file) + .fileUuid(fileCopyObjectRequest.getFileUuid()) + .build(); + } + +} diff --git a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/FileCopyObjectDto.java b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/FileCopyObjectDto.java new file mode 100644 index 0000000..6aa248a --- /dev/null +++ b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/FileCopyObjectDto.java @@ -0,0 +1,63 @@ +package cn.axzo.oss.manager.api.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * @author xudawei@axzo.cn + * @date 2025/3/10 + * @description 对象复制DTO + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class FileCopyObjectDto { + + /** + * 唯一标识 + * 没有唯一标识,可以与fileKey一致 + */ + private String fileUuid; + + /** + * 目标桶 + * 如果目标桶为空,则默认取源桶 + */ + private String targetBucketName; + + /** + * 目标目录 + * 如果目标目录为空,则默认在源目录同级 + */ + private String targetDict; + + /** + * 目标通道 + * 如果目标通道为空,则默认源通道 + */ + private String targetChannelCode; + + /** + * 文件名称 + */ + private String targetFileName; + + /** + * 桶名称 + */ + private String srcBucketName; + /** + * 桶文件目录 + */ + private String srcBucketKey; + /** + * 文件对象key + */ + private String srcFileKey; + +} diff --git a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/response/FileCopyObjectResponse.java b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/response/FileCopyObjectResponse.java new file mode 100644 index 0000000..1066216 --- /dev/null +++ b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/response/FileCopyObjectResponse.java @@ -0,0 +1,71 @@ +package cn.axzo.oss.manager.api.dto.response; + +import cn.axzo.oss.manager.api.dto.request.CopyObjectCloudDto; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * @author xudawei@axzo.cn + * @date 2025/3/10 + * @description 对象复制DTO + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class FileCopyObjectResponse { + + /** + * 唯一标识,没有唯一标识,可以与fileKey一致 + */ + @NotBlank(message = "唯一性标识不能为空") + private String fileUuid; + + /** + * 目标桶 + * 如果目标桶为空,则默认取源桶 + */ + private String targetFileKey; + + /** + * 目标通道 + * 如果目标通道为空,则默认源通道 + */ + private String targetChannelCode; + + /** + * 桶名称 + */ + private String bucketName; + /** + * 桶文件目录 + */ + private String bucketDict; + /** + * 文件对象key + */ + private String fileKey; + + /** + * 文件名称 + */ + private String fileName; + + public static FileCopyObjectResponse create(CopyObjectCloudDto item) { + return FileCopyObjectResponse.builder() + .fileUuid(item.getFileUuid()) + .targetFileKey(item.getTargetKey()) + .targetChannelCode(item.getTargetChannelCode()) +// .targetDownloadUrl(this.fileManager.fetchDownloadUrl(item.getFile().getb)) + .bucketName(item.getFile().getBucketName()) + .bucketDict(item.getFile().getDirectory()) + .fileKey(item.getFile().getFileUuid()) + .fileName(item.getFileName()) + .build(); + } + +} diff --git a/oss-manager/src/main/java/cn/axzo/oss/manager/impl/FileManagerImpl.java b/oss-manager/src/main/java/cn/axzo/oss/manager/impl/FileManagerImpl.java index 89833a6..6b69777 100644 --- a/oss-manager/src/main/java/cn/axzo/oss/manager/impl/FileManagerImpl.java +++ b/oss-manager/src/main/java/cn/axzo/oss/manager/impl/FileManagerImpl.java @@ -12,12 +12,13 @@ import cn.axzo.oss.integration.s3.AliOssService; import cn.axzo.oss.integration.s3.HuaWeiCloudService; import cn.axzo.oss.manager.api.FileManager; import cn.axzo.oss.manager.api.dto.PartETag; +import cn.axzo.oss.manager.api.dto.request.CopyObjectCloudDto; import cn.axzo.oss.manager.api.dto.request.MultipartUploadDto; import cn.axzo.oss.manager.api.dto.response.ManaGetObjectMetaResponse; import cn.axzo.oss.manager.api.dto.response.SignUrlDownloadResponse; import cn.axzo.oss.manager.api.vo.SignUrlUploadVo; -import com.alibaba.fastjson.JSON; import com.aliyun.oss.model.SimplifiedObjectMeta; +import com.obs.services.model.CopyObjectResult; import com.obs.services.model.ObjectMetadata; import com.obs.services.model.TemporarySignatureResponse; import lombok.extern.slf4j.Slf4j; @@ -346,4 +347,37 @@ public class FileManagerImpl implements FileManager { return url; } + /** + * 复制对象 + */ + @Override + public Boolean copyObjects(CopyObjectCloudDto dto) { + // 相同的通道-对象复制 + if (dto.getSrcChannelCode().equals(dto.getTargetChannelCode())) { + return this.copyObjectWhenSameChannel(dto); + } + // 不同的通道,通过url上传至云(通过url复制至云) + String url = this.uploadByUrl(dto.getTargetBucketName(), dto.getTargetKey(), dto.getFileName(), dto.getSrcUrl(), dto.getTargetChannelCode()); + return StringUtils.isBlank(url) ? false : true; + } + + /** + * 相同的通道-对象复制 + */ + public Boolean copyObjectWhenSameChannel(CopyObjectCloudDto dto) { + ChannelTypeEnum typeEnum = ChannelTypeEnum.getChannelTypeByChannelCode(dto.getSrcChannelCode()); + switch (typeEnum) { + case OBS:// 华为云 + CopyObjectResult huaweiResult = huaWeiCloudService.copyObject(dto.getSrcBucketName(), dto.getSrcBucketKey(), dto.getTargetBucketName(), dto.getTargetKey()); + return Objects.isNull(huaweiResult) ? false : true; + case OSS:// 阿里云 + com.aliyun.oss.model.CopyObjectResult aliResult = aliOssService.copyObject(dto.getSrcBucketName(), dto.getSrcBucketKey(), dto.getTargetBucketName(), dto.getTargetKey()); + return Objects.isNull(aliResult) ? false : true; + default: + BizException.error(CodeEnum.CHANNEL_TYPE_NOT_EXIST); + } + return false; + } + + } diff --git a/oss-service-api/src/main/java/cn/axzo/oss/service/api/FileService.java b/oss-service-api/src/main/java/cn/axzo/oss/service/api/FileService.java index 441ee11..2194d9f 100644 --- a/oss-service-api/src/main/java/cn/axzo/oss/service/api/FileService.java +++ b/oss-service-api/src/main/java/cn/axzo/oss/service/api/FileService.java @@ -4,6 +4,7 @@ import cn.axzo.framework.auth.domain.ContextInfo; import cn.axzo.oss.dal.entity.AppChannelBucket; import cn.axzo.oss.dal.entity.FileUploadConfig; import cn.axzo.oss.manager.api.dto.request.DeleteFileDto; +import cn.axzo.oss.manager.api.dto.request.FileCopyObjectDto; import cn.axzo.oss.manager.api.dto.request.FindFileKeyDto; import cn.axzo.oss.manager.api.dto.request.FindFileUrlDto; import cn.axzo.oss.manager.api.dto.request.GetObjectMetaDto; @@ -16,6 +17,7 @@ import cn.axzo.oss.manager.api.dto.request.ServerFileDownloadDto; import cn.axzo.oss.manager.api.dto.request.ServerFileUploadDto; import cn.axzo.oss.manager.api.dto.request.SignUrlDownloadDto; import cn.axzo.oss.manager.api.dto.request.SignUrlUploadDto; +import cn.axzo.oss.manager.api.dto.response.FileCopyObjectResponse; import cn.axzo.oss.manager.api.dto.response.FileInformationResponse; import cn.axzo.oss.manager.api.dto.response.FindFileKeyResponse; import cn.axzo.oss.manager.api.dto.response.FindFileUrlResponse; @@ -117,4 +119,11 @@ public interface FileService { * 授权给第三方下载 */ List signUrlDownloadNoFile(SignUrlDownloadDto dto); + + /** + * 复制文件 + * 1 不同通道,则通过url进行复制 + * 2 相同通道,则直接调用复制方法 + */ + List batchCopyObject(List dtoList, String appCode); } diff --git a/oss-service/src/main/java/cn/axzo/oss/service/impl/FileServiceImpl.java b/oss-service/src/main/java/cn/axzo/oss/service/impl/FileServiceImpl.java index d89782d..16c454a 100644 --- a/oss-service/src/main/java/cn/axzo/oss/service/impl/FileServiceImpl.java +++ b/oss-service/src/main/java/cn/axzo/oss/service/impl/FileServiceImpl.java @@ -1,5 +1,6 @@ package cn.axzo.oss.service.impl; +import cn.axzo.basics.common.util.AssertUtil; import cn.axzo.core.utils.converter.BeanConverter; import cn.axzo.framework.auth.domain.ContextInfo; import cn.axzo.log.platform.client.LogPlatClient; @@ -31,7 +32,9 @@ import cn.axzo.oss.manager.api.FileBusinessSceneManager; import cn.axzo.oss.manager.api.FileManager; import cn.axzo.oss.manager.api.FileUploadConfigManager; import cn.axzo.oss.manager.api.dto.PartETag; +import cn.axzo.oss.manager.api.dto.request.CopyObjectCloudDto; import cn.axzo.oss.manager.api.dto.request.DeleteFileDto; +import cn.axzo.oss.manager.api.dto.request.FileCopyObjectDto; import cn.axzo.oss.manager.api.dto.request.FindFileKeyDto; import cn.axzo.oss.manager.api.dto.request.FindFileUrlDto; import cn.axzo.oss.manager.api.dto.request.GetObjectMetaDto; @@ -44,6 +47,7 @@ import cn.axzo.oss.manager.api.dto.request.ServerFileDownloadDto; import cn.axzo.oss.manager.api.dto.request.ServerFileUploadDto; import cn.axzo.oss.manager.api.dto.request.SignUrlDownloadDto; import cn.axzo.oss.manager.api.dto.request.SignUrlUploadDto; +import cn.axzo.oss.manager.api.dto.response.FileCopyObjectResponse; import cn.axzo.oss.manager.api.dto.response.FileInformationResponse; import cn.axzo.oss.manager.api.dto.response.FindFileKeyResponse; import cn.axzo.oss.manager.api.dto.response.FindFileUrlResponse; @@ -54,11 +58,9 @@ import cn.axzo.oss.manager.api.dto.response.ServerFileDownloadResponse; import cn.axzo.oss.manager.api.dto.response.ServerFileUploadResponse; import cn.axzo.oss.manager.api.dto.response.SignUrlDownloadResponse; import cn.axzo.oss.manager.api.dto.response.SignUrlUploadResponse; -import cn.axzo.oss.manager.api.vo.SignUrlUploadVo; import cn.axzo.oss.service.api.FileService; import cn.axzo.oss.service.metafile.WithFileFactory; import cn.axzo.oss.service.metafile.WithFileService; -import cn.azxo.framework.common.model.CommonResponse; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.lang.Pair; @@ -89,6 +91,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; import java.util.stream.Collectors; import static cn.axzo.oss.common.constans.CommonConstants.APP_PRO_BUCKET_NAME; @@ -1258,4 +1261,106 @@ public class FileServiceImpl implements FileService { }).collect(Collectors.toList()); } + /** + * 复制文件 + * 1 不同通道,则通过url进行复制 + * 2 相同通道,则直接调用复制方法 + */ + public List batchCopyObject(List dtoList, String appCode) { + AssertUtil.isFalse(CollectionUtils.isEmpty(dtoList), "请求入参为空"); + //校验appCode + this.checkAppCode(appCode); + //通过fileKeys复制文件 + List cloudDtos = this.batchCopyObjectByFileKeys(dtoList); + //保存文件表 + return this.saveFiles(cloudDtos); + } + + /** + * 保存文件表 + */ + private List saveFiles(List cloudDtos) { + List responses = new ArrayList<>(); + + List fileList = cloudDtos.stream().map(item -> { + //复制文件-构建file对象 + File ossFile = this.buildFileWhenCopyObject(item); + //构建返回对象 + responses.add(FileCopyObjectResponse.create(item)); + return ossFile; + }).collect(Collectors.toList()); + + if (CollectionUtil.isEmpty(cloudDtos)) { + return Lists.newArrayList(); + } + this.fileDao.saveBatch(fileList); + return responses; + } + + /** + * 复制文件-构建file对象 + */ + private File buildFileWhenCopyObject(CopyObjectCloudDto item) { + File ossFile = new File(); + ossFile.setAppChannelBucketNo(item.getFile().getAppChannelBucketNo()); + ossFile.setAppCode(item.getFile().getAppCode()); + ossFile.setChannelCode(item.getTargetChannelCode()); + ossFile.setBucketName(item.getTargetBucketName()); + ossFile.setDirectory(item.getTargetBucketKey()); + ossFile.setStorageUnit(item.getFile().getStorageUnit()); + ossFile.setStorageSize(item.getFile().getStorageSize()); + ossFile.setFileFormat(item.getFile().getFileFormat()); + ossFile.setFileUuid(item.getTargetKey()); + ossFile.setFileUrl(this.fileManager.fetchDownloadUrl(item.getTargetBucketName(), Utility.generateFileKey(item.getTargetBucketKey(), item.getTargetKey(), item.getFile().getFileFormat()), item.getTargetChannelCode())); + ossFile.setUrlMd5(Utility.getMd5(ossFile.getFileUrl())); + ossFile.setStatus(FileStatusEnum.STATUS_UPLOAD_SUCCESS.getCode()); + ossFile.setFileName(item.getFileName()); + ossFile.setFileMd5(Utility.getMd5(ossFile.getFileUrl())); + return ossFile; + } + + /** + * 通过fileKeys复制文件 + */ + private List batchCopyObjectByFileKeys(List dtoList) { + List fileKeys = dtoList.stream() + .filter(item -> StringUtils.isNotBlank(item.getSrcFileKey())) + .map(FileCopyObjectDto::getSrcFileKey).collect(Collectors.toList()); + + Map reqFileKeyMap = dtoList.stream() + .filter(item -> StringUtils.isNotBlank(item.getSrcFileKey())) + .collect(Collectors.toMap(item -> item.getSrcFileKey(), Function.identity())); + //查询file数据表获取源对象 + List fileList = fileDao.getByFileUuids(fileKeys); + + if (CollectionUtil.isEmpty(fileList)) { + return Lists.newArrayList(); + } + //通过文件表,完成文件对象复制 + return this.doBatchCopyObjectByFiles(reqFileKeyMap, fileList); + } + + /** + * 通过文件表-文件对象复制 + * 注意,此方法是根据业务文件复制 + * 底层通用方法fileManager.copyObjects(dto); + */ + private List doBatchCopyObjectByFiles(Map reqFileKeyMap, List fileList) { + List returnList = Lists.newArrayList(); + for (File file : fileList) { + if (Objects.isNull(reqFileKeyMap.get(file.getFileUuid()))) { + continue; + } + FileCopyObjectDto fileCopyObjectRequest = reqFileKeyMap.get(file.getFileUuid()); + if (Objects.isNull(fileCopyObjectRequest)) { + continue; + } + CopyObjectCloudDto dto = CopyObjectCloudDto.create(fileCopyObjectRequest, file); + + this.fileManager.copyObjects(dto); + returnList.add(dto); + } + return returnList; + } + }