diff --git a/oss-client/src/main/java/cn/axzo/oss/client/Bootstrap.java b/oss-client/src/main/java/cn/axzo/oss/client/Bootstrap.java index 25cc699..c2c8bd9 100644 --- a/oss-client/src/main/java/cn/axzo/oss/client/Bootstrap.java +++ b/oss-client/src/main/java/cn/axzo/oss/client/Bootstrap.java @@ -13,7 +13,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients; @Slf4j @MapperScan(basePackages = {"cn.axzo.oss.dal.mapper", "cn.axzo.oss.client.icon.mapper"}) @EnableFeignClients(basePackages = {"cn.axzo.log.platform.client"}) -@SpringBootApplication(scanBasePackages = {"cn.axzo.oss"}) +@SpringBootApplication(scanBasePackages = {"cn.axzo.oss","com.axzo.framework"}) public class Bootstrap { public static void main(String[] args) { log.info("axzo oss begin starting..."); 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 ea8bdcb..2f2a617 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 @@ -22,16 +22,19 @@ import cn.axzo.oss.http.model.FindFileUrlResponse; import cn.axzo.oss.http.model.GetObjectMetaRequest; import cn.axzo.oss.http.model.GetObjectMetaResponse; import cn.axzo.oss.http.model.ServerFileDeleteRequest; +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.manager.api.dto.request.FindFileKeyDto; import cn.axzo.oss.manager.api.dto.request.FindFileUrlDto; 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.service.api.FileByUrlService; import cn.axzo.oss.service.api.FileService; import cn.azxo.framework.common.model.CommonResponse; import cn.hutool.json.JSONUtil; @@ -62,6 +65,9 @@ public class ServerFileController implements ServerFileServiceApi { @Autowired private FileService fileService; + @Autowired + private FileByUrlService fileByUrlService; + @Autowired private HttpServletRequest httpServletRequest; @@ -152,15 +158,30 @@ public class ServerFileController implements ServerFileServiceApi { throw new BizException(CodeEnum.URL_BUCKET_NAME_KEY_ALL_EMPTY); } - String bucketName = UrlUtil.fetchBucketName(request.getBucketName(), request.getUrl()); String bucketKey = UrlUtil.fetchBucketKey(request.getKey(), request.getUrl()); ManaGetObjectMetaResponse response = this.fileService.getObjectMeta(bucketName , bucketKey, request.getUrl() - , ChannelTypeEnum.OSS.getChannelCode()); + , this.swtchChannelCode(request.getUrl())); return CommonResponse.success(BeanConverter.convert(response, GetObjectMetaResponse.class)); } + /** + * 决定渠道 + */ + private String swtchChannelCode(String url) { + if (!StringUtils.hasText(url)) { + return ""; + } + if (url.toLowerCase().contains("aliyun")) { + return ChannelTypeEnum.OSS.getChannelCode(); + } + if (url.toLowerCase().contains("huaweicloud")) { + return ChannelTypeEnum.OBS.getChannelCode(); + } + return url; + } + /** * 文件元数据信息-批量 */ @@ -179,4 +200,18 @@ public class ServerFileController implements ServerFileServiceApi { } + + /** + * 通过URL上传文件 + * @param request + * @return + */ + @Override + public CommonResponse uploadByUrl(@Valid @RequestBody ServerFileUploadByUrlRequest request) { + ServerFileUploadByUrlDto dto = BeanConvertUtil.copyBean(request, ServerFileUploadByUrlDto.class); + // 获取feign调用请求头携带的用户信息 + String contextInfoLiteJsonStr = httpServletRequest.getHeader("X-CONTEXT-INFO-LITE"); + ContextInfo.LiteSaasContext liteSaasContext = JSONUtil.toBean(contextInfoLiteJsonStr, ContextInfo.LiteSaasContext.class); + return CommonResponse.success(BeanConverter.convert(fileByUrlService.uploadByUrl(dto.getAppCode(), dto.getBizScene(), dto.getFileName(), dto.getFileUrl(), dto.getChannelCode(), dto.getStyle(), liteSaasContext), ServerFileUploadResponse.class)); + } } diff --git a/oss-common/src/main/java/cn/axzo/oss/common/enums/ChannelTypeEnum.java b/oss-common/src/main/java/cn/axzo/oss/common/enums/ChannelTypeEnum.java index 8e2617e..5900d53 100644 --- a/oss-common/src/main/java/cn/axzo/oss/common/enums/ChannelTypeEnum.java +++ b/oss-common/src/main/java/cn/axzo/oss/common/enums/ChannelTypeEnum.java @@ -26,6 +26,7 @@ public enum ChannelTypeEnum { private final String channelCode; private final static Map channelCodeMap = Arrays.stream(ChannelTypeEnum.values()).collect(Collectors.toMap(ChannelTypeEnum::getChannelCode, Function.identity())); + private final static Map codeMap = Arrays.stream(ChannelTypeEnum.values()).collect(Collectors.toMap(ChannelTypeEnum::getCode, Function.identity())); ChannelTypeEnum(Integer code, String message, String channelCode) { this.code = code; @@ -45,9 +46,16 @@ public enum ChannelTypeEnum { } /** - * 通过code获取枚举 + * 通过channelCode获取枚举 */ public static ChannelTypeEnum getChannelTypeByChannelCode(String channelCode) { return channelCodeMap.get(channelCode); } + + /** + * 通过code获取枚举 + */ + public static ChannelTypeEnum getChannelTypeByCode(Integer code) { + return codeMap.get(code); + } } diff --git a/oss-common/src/main/java/cn/axzo/oss/common/utils/UrlUtil.java b/oss-common/src/main/java/cn/axzo/oss/common/utils/UrlUtil.java index 7e8870b..5609600 100644 --- a/oss-common/src/main/java/cn/axzo/oss/common/utils/UrlUtil.java +++ b/oss-common/src/main/java/cn/axzo/oss/common/utils/UrlUtil.java @@ -10,8 +10,14 @@ import org.apache.commons.lang3.StringUtils; public class UrlUtil { - public static String HTTP = "http:"; - public static String HTTPS = "https:"; + public static String HTTP_START = "http:"; + public static String HTTPS_START = "https:"; + public static String X_OSS_PROCESS = "x-oss-process"; + public static String X_OSS_PROCESS_APPEND = "?x-oss-process="; + public static String X_IMAGE_PROCESS = "x-image-process"; + public static String X_IMAGE_PROCESS_APPEND = "?x-image-process="; + public static String ALIYUN = "aliyun"; + public static String HUAWEICLOUD = "huaweicloud"; /** * http的url替换成https @@ -20,8 +26,8 @@ public class UrlUtil { if (StringUtils.isBlank(str)) { return str; } - if (str.startsWith(HTTP)) { - return HTTPS + str.substring(str.indexOf(HTTP) + HTTP.length()); + if (str.startsWith(HTTP_START)) { + return HTTPS_START + str.substring(str.indexOf(HTTP_START) + HTTP_START.length()); } return str; } @@ -47,11 +53,16 @@ public class UrlUtil { if (org.springframework.util.StringUtils.hasText(bucketKey)) { return bucketKey; } - if (org.springframework.util.StringUtils.hasText(url)) { - if (url.contains(".") && url.contains("//")) { - String urlPathNoHttp = url.split("//")[1]; - return urlPathNoHttp.substring(urlPathNoHttp.indexOf("/") + 1); - } + + if (StringUtils.isBlank(url)) { + return url; + } + if (url.contains("?")) { + url = url.split("\\?")[0]; + } + if (url.contains(".") && url.contains("//")) { + String urlPathNoHttp = url.split("//")[1]; + return urlPathNoHttp.substring(urlPathNoHttp.indexOf("/") + 1); } return url; } 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 59d6a1a..b3ce9d0 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 @@ -14,6 +14,7 @@ import cn.axzo.oss.http.model.FindFileUrlResponse; import cn.axzo.oss.http.model.GetObjectMetaRequest; import cn.axzo.oss.http.model.GetObjectMetaResponse; import cn.axzo.oss.http.model.ServerFileDeleteRequest; +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; @@ -98,4 +99,12 @@ public interface ServerFileServiceApi { @RequestMapping(value = "api/server/batchGetObjectMeta", method = RequestMethod.POST) CommonResponse batchGetObjectMeta(BatchGetObjectMetaRequest request); + /** + * 通过URL上传文件 + * @param request + * @return + */ + @RequestMapping(value = "/api/v1/server/uploadByUrl", method = RequestMethod.POST) + CommonResponse uploadByUrl(ServerFileUploadByUrlRequest request); + } diff --git a/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlDownloadRequest.java b/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlDownloadRequest.java index a2b7792..45bde52 100644 --- a/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlDownloadRequest.java +++ b/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlDownloadRequest.java @@ -32,4 +32,14 @@ public class ApiSignUrlDownloadRequest { */ private String bizScene; + /** + * 图片样式,比如x-oss-process=image/auto-orient,1/resize,p_50/quality,q_30 + */ + private String style; + + /** + * 是否带上文件名称 + */ + private Boolean hasFileName = true; + } diff --git a/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlUploadRequest.java b/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlUploadRequest.java index aef2959..32d3b14 100644 --- a/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlUploadRequest.java +++ b/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlUploadRequest.java @@ -56,4 +56,9 @@ public class ApiSignUrlUploadRequest { * 过期时间 */ private Long expiration; + + /** + * 是否带上文件名称 + */ + private Boolean hasFileName = true; } diff --git a/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlUploadResponse.java b/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlUploadResponse.java index 1c3e762..7313f0d 100644 --- a/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlUploadResponse.java +++ b/oss-http-api/src/main/java/cn/axzo/oss/http/model/ApiSignUrlUploadResponse.java @@ -46,4 +46,9 @@ public class ApiSignUrlUploadResponse { * 下载url */ private String downloadSignUrl; + + /** + * 文件名称 + */ + private String fileName; } diff --git a/oss-http-api/src/main/java/cn/axzo/oss/http/model/ServerFileUploadByUrlRequest.java b/oss-http-api/src/main/java/cn/axzo/oss/http/model/ServerFileUploadByUrlRequest.java new file mode 100644 index 0000000..7c530c2 --- /dev/null +++ b/oss-http-api/src/main/java/cn/axzo/oss/http/model/ServerFileUploadByUrlRequest.java @@ -0,0 +1,35 @@ +package cn.axzo.oss.http.model; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 服务端文件上传请求类 + * + * @author zhaoyong + * @see ServerFileUploadByUrlRequest + * @since 2021-07-23 10:35 + */ +@Data +public class ServerFileUploadByUrlRequest { + + @NotBlank(message = "appCode must not be null") + private String appCode; + @NotBlank(message = "bizScene must not be null") + private String bizScene; + @NotBlank(message = "fileName must not be null") + @Length(max = 128, message = "文件名不能超过128个字符") + private String fileName; + @NotNull(message = "fileUrl must not be null") + private String fileUrl; + + private Integer channelCode; + /** + * 图片样式,比如x-oss-process=image/auto-orient,1/resize,p_50/quality,q_30 + */ + private String style; + +} 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 e557f3d..2d9dd56 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 @@ -14,7 +14,7 @@ public interface AliOssService extends BaseS3Service { /** * 授权给第三方下载 */ - String downloadSignUrl(String bucketName, String key, Long expireSecond, String fileName); + String downloadSignUrl(String bucketName, String key, Long expireSecond, String fileName, boolean hasFileName); /** * 授权给第三方-上传 @@ -35,4 +35,9 @@ public interface AliOssService extends BaseS3Service { */ SimplifiedObjectMeta getObjectMeta(String bucketName, String key, String url); + + /** + * 通过url上传至OBS + */ + String uploadByUrl(String bucketName, String tgtFileKey, String fileName, String url); } 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 01cbc0b..ec41f89 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.ObjectMetadata; import com.obs.services.model.TemporarySignatureResponse; import java.io.InputStream; @@ -24,7 +25,7 @@ public interface HuaWeiCloudService { /** * 授权给第三方-下载 */ - String downloadSignUrl(String bucketName, String key, Long expireSecond, String fileName); + String downloadSignUrl(String bucketName, String key, Long expireSecond, String fileName, String style, boolean hasFilename); /** * 授权给第三方-上传 @@ -40,4 +41,14 @@ public interface HuaWeiCloudService { * 获取url */ String getUrl(String bucketName, String tgtFileKey); + + /** + * 通过url上传OBS + */ + String uploadFileByUrl(String bucketName, String key, String fileName, String url); + + /** + * 元数据 + */ + ObjectMetadata getObjectMeta(String bucketName, String key, String url); } 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 3c6ddac..9c9e85d 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 @@ -289,7 +289,7 @@ public class AliOssServiceImpl implements AliOssService { * 授权给第三方-下载 */ @Override - public String downloadSignUrl(String bucketName, String key, Long expireSecond, String fileName) { + public String downloadSignUrl(String bucketName, String key, Long expireSecond, String fileName, boolean hasFileName) { Date date = new Date(); date.setTime(date.getTime() + expireSecond * 1000); try { @@ -297,7 +297,9 @@ public class AliOssServiceImpl implements AliOssService { GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, key, HttpMethod.GET); request.setExpiration(date); ResponseHeaderOverrides responseHeaderOverrides = new ResponseHeaderOverrides(); - responseHeaderOverrides.setContentDisposition("attachment;filename=\"" + URLEncoder.encode(fileName, "utf-8") + "\""); + if (hasFileName) { + responseHeaderOverrides.setContentDisposition("attachment;filename=\"" + URLEncoder.encode(fileName, "utf-8") + "\""); + } request.setResponseHeaders(responseHeaderOverrides); URL url = aliOssClient.getClient().generatePresignedUrl(request); log.info("aliyun downloadSignUrl result, bucketName:{}, key:{}, url:{}", bucketName, key, JsonUtil.obj2Str(url)); @@ -390,4 +392,29 @@ public class AliOssServiceImpl implements AliOssService { return new SimplifiedObjectMeta(); } } + + /** + * 通过url上传至OSS + */ + @Override + public String uploadByUrl(String bucketName, String tgtFileKey, String fileName, String url) { + OSS client = aliOssClient.getClient(); + try { + InputStream srcStream = new URL(url).openStream(); + // 创建上传文件的元信息,通过文件云信息设置HTTP Header + ObjectMetadata metadata = new ObjectMetadata(); + // 设置内容被下载时的名称 + metadata.setContentDisposition("attachment;filename=\"" + URLEncoder.encode(fileName, "utf-8") + "\""); + metadata.setContentEncoding("utf-8"); + client.putObject(bucketName, tgtFileKey, srcStream, metadata); + } catch (OSSException e) { + LogUtil.error("uploadByStream OSSException", e); + return ""; + } catch (Exception e) { + LogUtil.error("uploadByStream ClientException", e); + return ""; + } + + return getUrl(bucketName, tgtFileKey); + } } 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 ffc0909..ca562c1 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 @@ -1,7 +1,7 @@ package cn.axzo.oss.integration.s3.impl; -import cn.axzo.oss.common.enums.FileDownloadTypeEnum; import cn.axzo.oss.common.enums.CodeEnum; +import cn.axzo.oss.common.enums.FileDownloadTypeEnum; import cn.axzo.oss.common.exception.BizException; import cn.axzo.oss.common.utils.JsonUtil; import cn.axzo.oss.integration.s3.HuaWeiCloudService; @@ -13,12 +13,33 @@ import cn.hutool.json.JSONUtil; import com.google.common.collect.Maps; import com.obs.services.ObsClient; import com.obs.services.exception.ObsException; -import com.obs.services.model.*; +import com.obs.services.model.CompleteMultipartUploadRequest; +import com.obs.services.model.CompleteMultipartUploadResult; +import com.obs.services.model.DeleteObjectResult; +import com.obs.services.model.DownloadFileRequest; +import com.obs.services.model.DownloadFileResult; +import com.obs.services.model.GetObjectRequest; +import com.obs.services.model.HttpMethodEnum; +import com.obs.services.model.InitiateMultipartUploadRequest; +import com.obs.services.model.InitiateMultipartUploadResult; +import com.obs.services.model.ObjectMetadata; +import com.obs.services.model.ObsObject; +import com.obs.services.model.PartEtag; +import com.obs.services.model.PutObjectRequest; +import com.obs.services.model.PutObjectResult; +import com.obs.services.model.TemporarySignatureRequest; +import com.obs.services.model.TemporarySignatureResponse; +import com.obs.services.model.UploadFileRequest; +import com.obs.services.model.UploadPartRequest; +import com.obs.services.model.UploadPartResult; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import java.io.InputStream; +import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Date; @@ -331,14 +352,21 @@ public class HuaWeiCloudServiceImpl implements HuaWeiCloudService { * 授权给第三方-下载 */ @Override - public String downloadSignUrl(String bucketName, String key, Long expireSeconds, String fileName) { + public String downloadSignUrl(String bucketName, String key, Long expireSeconds, String fileName, String style, boolean hasFilename) { TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, expireSeconds); request.setBucketName(bucketName); request.setObjectKey(key); Map queryParams = Maps.newHashMap(); try { - queryParams.put("response-content-disposition", "attachment;filename=\"" + URLEncoder.encode(fileName, "utf-8") + "\""); - request.setQueryParams(queryParams); + if (hasFilename) { + queryParams.put("response-content-disposition", "attachment;filename=\"" + URLEncoder.encode(fileName, "utf-8") + "\""); + } + if (StringUtils.hasText(style)) { + queryParams.put("x-image-process", style); + } + if (!CollectionUtils.isEmpty(queryParams)) { + request.setQueryParams(queryParams); + } log.info("huawei cloud downloadSignUrl params, bucketName:{}, key:{}, request:{}", bucketName, key, JsonUtil.obj2Str(request)); TemporarySignatureResponse response = huaWeiCloudObsClient.getClient().createTemporarySignature(request); log.info("huawei cloud downloadSignUrl result, bucketName:{}, key:{}, request:{}", bucketName, key, JsonUtil.obj2Str(response)); @@ -403,4 +431,45 @@ public class HuaWeiCloudServiceImpl implements HuaWeiCloudService { return allBuilder.toString(); } + + /** + * 通过URL上传文件到OBS + */ + @Override + public String uploadFileByUrl(String bucketName, String key, String fileName, String url) { + ObsClient obsClient = huaWeiCloudObsClient.getClient(); + try { + InputStream srcStream = new URL(url).openStream(); + // 创建上传文件的元信息,通过文件云信息设置HTTP Header + ObjectMetadata metadata = new ObjectMetadata(); + // 设置内容被下载时的名称 + metadata.setContentDisposition("attachment;filename=\"" + URLEncoder.encode(fileName, "utf-8") + "\""); + metadata.setContentEncoding("utf-8"); + PutObjectResult putObjectResult = obsClient.putObject(bucketName, key, srcStream, metadata); + return putObjectResult.getObjectUrl(); + } catch (Exception e) { + log.error("通过url上传 exception,bucketName:{}, key:{}, fileName:{},url:{}", bucketName, key, fileName, url); + return ""; + } + + + } + + /** + * 元数据 + */ + @Override + public ObjectMetadata getObjectMeta(String bucketName, String key, String url) { + try { + if (org.apache.commons.lang3.StringUtils.isNotBlank(url) && url.toLowerCase().startsWith("http") && url.toLowerCase().contains("huaweicloud")) { + return huaWeiCloudObsClient.getClient().getObjectMetadata(bucketName, key); + } + log.info("huaweicloud-none-endpoint getObjectMeta params, bucketName:{}, key:{},url:{}", bucketName, key, url); + return new ObjectMetadata(); + } catch (Exception e) { + log.warn("huaweicloud-getObjectMeta-exception, bucketName:{}, key:{}", bucketName, key, e); + return new ObjectMetadata(); + } + } + } 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 390cf1e..38520e0 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 @@ -60,12 +60,12 @@ public interface FileManager { /** * 根据华为云/阿里云,授权给第三方下载 */ - String signUrlDownload(String bucketName, String key, Long expireSecond, String channelCode, String fileName); + String signUrlDownload(String bucketName, String key, Long expireSecond, String channelCode, String fileName, String style, boolean hasFileName); /** * 根据华为云/阿里云,授权给第三方上传 */ - SignUrlUploadVo signUrlUpload(String bucketName, String key, String fileName, Long expireSecond, String channelCode, String contentType, String bucketType); + SignUrlUploadVo signUrlUpload(String bucketName, String key, String fileName, Long expireSecond, String channelCode, String contentType, String bucketType, boolean hasFileName); /** * 删除File @@ -81,4 +81,10 @@ public interface FileManager { * 元数据 */ ManaGetObjectMetaResponse getObjectMeta(String bucketName, String key, String url,String channelCode); + + /** + * 通过url上传至云 + */ + String uploadByUrl(String bulkName, String keyPath, String fileName, String url, + String channelCode); } diff --git a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/ServerFileUploadByUrlDto.java b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/ServerFileUploadByUrlDto.java new file mode 100644 index 0000000..ae07c8a --- /dev/null +++ b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/ServerFileUploadByUrlDto.java @@ -0,0 +1,33 @@ +package cn.axzo.oss.manager.api.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * @program: oss + * @description: 文件上传 + * @author: mr.jie + * @date: 2021-07-29 18:31 + **/ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class ServerFileUploadByUrlDto { + private String appCode; + private String bizScene; + private String fileName; + private String fileUrl; + private String filePath; + private Integer channelCode; + + /** + * 图片样式,比如x-oss-process=image/auto-orient,1/resize,p_50/quality,q_30 + */ + private String style; + +} diff --git a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/SignUrlDownloadDto.java b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/SignUrlDownloadDto.java index 757887e..1b40527 100644 --- a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/SignUrlDownloadDto.java +++ b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/SignUrlDownloadDto.java @@ -34,4 +34,13 @@ public class SignUrlDownloadDto { */ private String bizScene; + /** + * 图片样式,比如x-oss-process=image/auto-orient,1/resize,p_50/quality,q_30 + */ + private String style; + /** + * 是否带上文件名称 + */ + private Boolean hasFileName = true; + } diff --git a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/SignUrlUploadDto.java b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/SignUrlUploadDto.java index 52d75d8..5afb663 100644 --- a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/SignUrlUploadDto.java +++ b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/request/SignUrlUploadDto.java @@ -57,4 +57,9 @@ public class SignUrlUploadDto { * 过期时间 */ private Long expiration; + + /** + * 是否带上文件名称 + */ + private Boolean hasFileName = true; } diff --git a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/response/SignUrlUploadResponse.java b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/response/SignUrlUploadResponse.java index e947ccd..4f0682f 100644 --- a/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/response/SignUrlUploadResponse.java +++ b/oss-manager-api/src/main/java/cn/axzo/oss/manager/api/dto/response/SignUrlUploadResponse.java @@ -46,4 +46,9 @@ public class SignUrlUploadResponse { * 下载url */ private String downloadSignUrl; + + /** + * 文件名称 + */ + private String fileName; } 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 e68527a..ae55f60 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 @@ -16,6 +16,7 @@ import cn.axzo.oss.manager.api.dto.request.MultipartUploadDto; import cn.axzo.oss.manager.api.dto.response.ManaGetObjectMetaResponse; import cn.axzo.oss.manager.api.vo.SignUrlUploadVo; import com.aliyun.oss.model.SimplifiedObjectMeta; +import com.obs.services.model.ObjectMetadata; import com.obs.services.model.TemporarySignatureResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; @@ -150,13 +151,13 @@ public class FileManagerImpl implements FileManager { * 根据华为云/阿里云,授权给第三方下载 */ @Override - public String signUrlDownload(String bucketName, String key, Long expireSecond, String channelCode, String fileName) { + public String signUrlDownload(String bucketName, String key, Long expireSecond, String channelCode, String fileName, String style, boolean hasFileName) { ChannelTypeEnum typeEnum = ChannelTypeEnum.getChannelTypeByChannelCode(channelCode); switch (typeEnum) { case OBS:// 华为云 - return huaWeiCloudService.downloadSignUrl(bucketName, key, expireSecond, fileName); + return huaWeiCloudService.downloadSignUrl(bucketName, key, expireSecond, fileName, style, hasFileName); case OSS:// 阿里云 - return aliOssService.downloadSignUrl(bucketName, key, expireSecond, fileName); + return aliOssService.downloadSignUrl(bucketName, key, expireSecond, fileName, hasFileName); default: BizException.error(CodeEnum.CHANNEL_TYPE_NOT_EXIST); } @@ -167,7 +168,7 @@ public class FileManagerImpl implements FileManager { * 根据华为云/阿里云,授权给第三方上传 */ @Override - public SignUrlUploadVo signUrlUpload(String bucketName, String key, String fileName, Long expireSecond, String channelCode, String contentType, String bucketType) { + public SignUrlUploadVo signUrlUpload(String bucketName, String key, String fileName, Long expireSecond, String channelCode, String contentType, String bucketType, boolean hasFileName) { ChannelTypeEnum typeEnum = ChannelTypeEnum.getChannelTypeByChannelCode(channelCode); switch (typeEnum) { case OBS:// 华为云 @@ -178,13 +179,13 @@ public class FileManagerImpl implements FileManager { .host(response.getActualSignedRequestHeaders().get(HUAWEI_CLOUD_SIGNURL_UPLOAD_HOST_KEY)) .contentType(response.getActualSignedRequestHeaders().get(HUAWEI_CLOUD_SIGNURL_UPLOAD_CONTENT_TYPE_KEY)) .channelCode(channelCode) - .downloadSignUrl(UrlUtil.httpToHttps(this.downloadHuaweiSignUrl(bucketName, key, expireSecond, bucketType, fileName))) + .downloadSignUrl(UrlUtil.httpToHttps(this.downloadHuaweiSignUrl(bucketName, key, expireSecond, bucketType, fileName, hasFileName))) .build(); case OSS:// 阿里云 return SignUrlUploadVo.builder() .signUrl(UrlUtil.httpToHttps(aliOssService.uploadSignUrl(bucketName, key, fileName,expireSecond))) .channelCode(channelCode) - .downloadSignUrl(UrlUtil.httpToHttps(this.downloadAliyunSignUrl(bucketName, key, expireSecond, bucketType, fileName))) + .downloadSignUrl(UrlUtil.httpToHttps(this.downloadAliyunSignUrl(bucketName, key, expireSecond, bucketType, fileName, hasFileName))) .build(); default: BizException.error(CodeEnum.CHANNEL_TYPE_NOT_EXIST); @@ -195,10 +196,10 @@ public class FileManagerImpl implements FileManager { /** * 构建华为云下载地址 */ - private String downloadHuaweiSignUrl(String bucketName, String key, Long expireSecond, String bucketType, String fileName) { + private String downloadHuaweiSignUrl(String bucketName, String key, Long expireSecond, String bucketType, String fileName, boolean hasFileName) { switch (BucketTypeEnum.getByCode(bucketType)) { case PRIVATE_BUCKET: - return huaWeiCloudService.downloadSignUrl(bucketName, key, expireSecond, fileName); + return huaWeiCloudService.downloadSignUrl(bucketName, key, expireSecond, fileName, null, hasFileName); case PUBLIC_BUCKET: return huaWeiCloudService.getUrl(bucketName, key); default: @@ -210,10 +211,10 @@ public class FileManagerImpl implements FileManager { /** * 构建阿里云下载地址 */ - private String downloadAliyunSignUrl(String bucketName, String key, Long expireSecond, String bucketType, String fileName) { + private String downloadAliyunSignUrl(String bucketName, String key, Long expireSecond, String bucketType, String fileName, boolean hasFileName) { switch (BucketTypeEnum.getByCode(bucketType)) { case PRIVATE_BUCKET: - return aliOssService.downloadSignUrl(bucketName, key, expireSecond, fileName); + return aliOssService.downloadSignUrl(bucketName, key, expireSecond, fileName, hasFileName); case PUBLIC_BUCKET: return aliOssService.getUrl(bucketName, key); default: @@ -263,7 +264,8 @@ public class FileManagerImpl implements FileManager { ChannelTypeEnum typeEnum = ChannelTypeEnum.getChannelTypeByChannelCode(channelCode); switch (typeEnum) { case OBS:// 华为云 - return ManaGetObjectMetaResponse.builder().build(); + ObjectMetadata objectMetadata = huaWeiCloudService.getObjectMeta(bucketName, key, url); + return ManaGetObjectMetaResponse.builder().bucketName(bucketName).key(key).size(objectMetadata.getContentLength()).build(); case OSS:// 阿里云 SimplifiedObjectMeta simplifiedObjectMeta = aliOssService.getObjectMeta(bucketName, key, url); return ManaGetObjectMetaResponse.builder().bucketName(bucketName).key(key).size(simplifiedObjectMeta.getSize()).build(); @@ -272,4 +274,26 @@ public class FileManagerImpl implements FileManager { } return ManaGetObjectMetaResponse.builder().build(); } + + /** + * 通过url上传至云 + */ + public String uploadByUrl(String bulkName, String keyPath, String fileName, String url, + String channelCode) { + try { + switch (ChannelTypeEnum.getChannelTypeByChannelCode(channelCode)) { + case OSS: + return aliOssService.uploadByUrl(bulkName,keyPath, fileName, url); + case OBS: + return huaWeiCloudService.uploadFileByUrl(bulkName, keyPath, fileName, url); + default: + BizException.error(CodeEnum.CHANNEL_TYPE_NOT_EXIST); + } + } catch (Exception e) { + log.error("通过url上传至云 exception,bulkName:{}, key:{}, fileName:{}, url:{},channelCode:{}" + , bulkName, keyPath, fileName, url, channelCode, e); + } + return StringUtils.EMPTY; + } + } diff --git a/oss-service-api/src/main/java/cn/axzo/oss/service/api/FileByUrlService.java b/oss-service-api/src/main/java/cn/axzo/oss/service/api/FileByUrlService.java new file mode 100644 index 0000000..8c4b04b --- /dev/null +++ b/oss-service-api/src/main/java/cn/axzo/oss/service/api/FileByUrlService.java @@ -0,0 +1,41 @@ +package cn.axzo.oss.service.api; + +import cn.axzo.framework.auth.domain.ContextInfo; +import cn.axzo.oss.manager.api.dto.request.DeleteFileDto; +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.MultipartUploadCompleteDto; +import cn.axzo.oss.manager.api.dto.request.MultipartUploadDto; +import cn.axzo.oss.manager.api.dto.request.MultipartUploadFileDto; +import cn.axzo.oss.manager.api.dto.request.MultipartUploadInitDto; +import cn.axzo.oss.manager.api.dto.request.ServerFileDeleteDto; +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.FileInformationResponse; +import cn.axzo.oss.manager.api.dto.response.FindFileKeyResponse; +import cn.axzo.oss.manager.api.dto.response.FindFileUrlResponse; +import cn.axzo.oss.manager.api.dto.response.MultipartUploadInitResponse; +import cn.axzo.oss.manager.api.dto.response.MultipartUploadResponse; +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 org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * @Author admin + * @Description + * @Date 2021/7/28 22:48 + * @Version 0.0.1 + **/ +public interface FileByUrlService { + + /** + * 上传-通过url + */ + ServerFileUploadResponse uploadByUrl(String appCode, String bizScene, String fileName, String url,Integer channelType, String style, ContextInfo.LiteSaasContext liteSaasContext); +} diff --git a/oss-service/pom.xml b/oss-service/pom.xml index ed085cf..96e7fb7 100644 --- a/oss-service/pom.xml +++ b/oss-service/pom.xml @@ -26,6 +26,12 @@ com.huaweicloud esdk-obs-java-bundle + + + cn.axzo.framework.logging + axzo-common-trace + 1.0.0-SNAPSHOT + diff --git a/oss-service/src/main/java/cn/axzo/oss/service/impl/FileByUrlServiceImpl.java b/oss-service/src/main/java/cn/axzo/oss/service/impl/FileByUrlServiceImpl.java new file mode 100644 index 0000000..999dea8 --- /dev/null +++ b/oss-service/src/main/java/cn/axzo/oss/service/impl/FileByUrlServiceImpl.java @@ -0,0 +1,349 @@ +package cn.axzo.oss.service.impl; + +import cn.axzo.framework.auth.domain.ContextInfo; +import cn.axzo.log.platform.client.LogPlatClient; +import cn.axzo.log.platform.client.model.OperateLogReq; +import cn.axzo.oss.common.constans.CommonConstants; +import cn.axzo.oss.common.constans.CommonConstants.FileStatus; +import cn.axzo.oss.common.constans.CommonConstants.TableDelete; +import cn.axzo.oss.common.enums.BucketTypeEnum; +import cn.axzo.oss.common.enums.ChannelTypeEnum; +import cn.axzo.oss.common.enums.CodeEnum; +import cn.axzo.oss.common.enums.FileClassEnum; +import cn.axzo.oss.common.enums.FileDownloadTypeEnum; +import cn.axzo.oss.common.enums.FileStatusEnum; +import cn.axzo.oss.common.enums.FileUploadTypeEnum; +import cn.axzo.oss.common.exception.BizException; +import cn.axzo.oss.common.utils.JsonUtil; +import cn.axzo.oss.common.utils.UrlUtil; +import cn.axzo.oss.common.utils.Utility; +import cn.axzo.oss.dal.entity.AppChannelBucket; +import cn.axzo.oss.dal.entity.File; +import cn.axzo.oss.dal.entity.FileApp; +import cn.axzo.oss.dal.entity.FileBusinessScene; +import cn.axzo.oss.dal.entity.FileUploadConfig; +import cn.axzo.oss.dal.repository.FileAppDao; +import cn.axzo.oss.dal.repository.FileDao; +import cn.axzo.oss.integration.s3.config.HuaWeiCloudObsConfig; +import cn.axzo.oss.manager.api.AppChannelBucketManager; +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.DeleteFileDto; +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.MultipartUploadCompleteDto; +import cn.axzo.oss.manager.api.dto.request.MultipartUploadDto; +import cn.axzo.oss.manager.api.dto.request.MultipartUploadFileDto; +import cn.axzo.oss.manager.api.dto.request.MultipartUploadInitDto; +import cn.axzo.oss.manager.api.dto.request.ServerFileDeleteDto; +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.FileInformationResponse; +import cn.axzo.oss.manager.api.dto.response.FindFileKeyResponse; +import cn.axzo.oss.manager.api.dto.response.FindFileUrlResponse; +import cn.axzo.oss.manager.api.dto.response.MultipartUploadInitResponse; +import cn.axzo.oss.manager.api.dto.response.MultipartUploadResponse; +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.FileByUrlService; +import cn.axzo.oss.service.api.FileService; +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import static cn.axzo.oss.common.constans.CommonConstants.APP_PRO_BUCKET_NAME; + +/** + * @Author admin + * @Description + * @Date 2021/7/28 22:48 + * @Version 0.0.1 + **/ +@Service +@RefreshScope +@Slf4j +public class FileByUrlServiceImpl implements FileByUrlService { + + private static final String FILE_UPLOAD_CODE = "OSS_FILE_UPLOAD"; + private static final String FILE_UPLOAD_NAME = "文件上传"; + + @Autowired + private FileDao fileDao; + + @Autowired + private FileManager fileManager; + + @Autowired + private FileAppDao fileAppDao; + + @Autowired + private AppChannelBucketManager appChannelBucketManager; + + @Autowired + private FileBusinessSceneManager fileBusinessSceneManager; + + @Autowired + private FileUploadConfigManager fileUploadConfigManager; + + @Autowired + private Environment environment; + + @Autowired + private LogPlatClient logPlatClient; + + @Autowired + private FileService fileService; + + @Value("${sign.url.download.expire.second:2000}") + private Long SIGN_URL_DOWNLOAD_EXPIRE_SECOND; + + /** + * 通过url上传 + */ + @Override + public ServerFileUploadResponse uploadByUrl(String appCode, String bizScene, String fileName, String url, Integer channelType,String style, ContextInfo.LiteSaasContext liteSaasContext) { + log.info("uploadByUrl params,appCode:{},bizScene:{}, fileName:{},url:{},channelType:{}", appCode, bizScene, fileName, url, channelType); + Long start = System.currentTimeMillis(); + //重新构建链接 + url = this.rebuildUrl(url, style); + log.info("uploadByUrl-style-url:{}",url); + File ossFile = uploadFileAndGetFile(appCode, bizScene, fileName, url, channelType); + //操作日志记录 + operateLog(this.buildUploadParams(appCode, bizScene, fileName, url, channelType), "", FILE_UPLOAD_CODE, FILE_UPLOAD_NAME, liteSaasContext); + ServerFileUploadResponse response = this.buildResponse(ossFile); + log.info("uploadByUrl result,appCode:{},bizScene:{}, fileName:{},url:{},channelType:{}, response:{},times:{}" + , appCode, bizScene, fileName, url, channelType, JsonUtil.obj2Str(response), System.currentTimeMillis() - start); + return response; + } + + /** + * url:https://xxx,则直接返回 + * url:fileKey,则返回https://链接 + */ + private String rebuildUrl(String url, String style) { + if (StringUtils.isNotBlank(url)) { + SignUrlDownloadDto signUrlDownloadDto = SignUrlDownloadDto.builder().fileKeys(Lists.newArrayList(url)).style(style).build(); + + List responseList = fileService.signUrlDownload(signUrlDownloadDto); + if (CollectionUtil.isNotEmpty(responseList)) { + return responseList.get(0).getSignUrl(); + } + } + return StringUtils.EMPTY; + } + + /** + * 操作日志记录 + */ + private void operateLog(String param, String serviceName, String featureCode, String featureName, ContextInfo.LiteSaasContext liteSaasContext) { + if (liteSaasContext == null || liteSaasContext.getIdentityId() == null || liteSaasContext.getIdentityType() == null) { + log.warn("获取用户上下文信息失败"); + return; + } + //记录日志 + try { + OperateLogReq log = OperateLogReq.builder() + .serviceName(StringUtils.isNotEmpty(serviceName) ? serviceName : environment.getProperty("spring.application.name")) + .featureCode(featureCode) + .featureName(featureName) + .operateTime(new Date()) + .identityId(liteSaasContext.getIdentityId()) + .identityType(liteSaasContext.getIdentityType()) + .operateParam(param) + .workspaceId(liteSaasContext.getWorkspaceId()) + .operateType(1) + .build(); + logPlatClient.createOperateLog(log); + } catch (Exception e) { + log.warn("服务间调用异常-发送日志失败: errMsg={}.", e.getMessage()); + } + } + + /** + * 上传并获取文件 + */ + private File uploadFileAndGetFile(String appCode, String bizScene, String fileName, String url, Integer channelType) { + log.info("update channelType:{}, fileName:{}, appCode:{}, bizScene:{}", + channelType, fileName, appCode, bizScene); + + // 检查appCode + checkAppCode(appCode); + + // 通过appcode获取文件渠道桶信息 + AppChannelBucket appChannelBucket = appChannelBucketManager.getByAppCode(appCode, channelType); + + // 通过渠道桶编码获取到具体文件业务场景 + FileBusinessScene scene = fileBusinessSceneManager + .getByBucketNoAndScene(appChannelBucket.getAppChannelBucketNo(), bizScene); + + // 通过渠道码和桶名称获取获取指定上传配置 + FileUploadConfig fileUploadConfig = fileUploadConfigManager + .getByUploadConfig(scene.getAppChannelBucketNo(), scene.getDirectory()); + + // 上传文件并生成file对象 + return generateFile(fileUploadConfig, fileName,url, fileUploadConfig.getChannelCode(), appChannelBucket.getBucketType(), scene.getDownloadExpiration()); + } + + /** + * 检查appCode是否有效 + * + * @param appCode + */ + private void checkAppCode(final String appCode) { + log.info("checkAppCode appCode = {}", appCode); + FileApp fileApp = fileAppDao.getByAppCode(appCode); + BizException.error(Utility.objIsNotNull(fileApp), CodeEnum.FILE_APP_IS_EMPTY); + } + + /** + * 生产File对象 + */ + private File generateFile(FileUploadConfig fileUploadConfig, String fileName, String url, String channelCode, String bucketType, Long expire) { + // 判断容量 + String fileConform = isFileConform(fileUploadConfig, fileName); + String uuid = Utility.getUUID(); + // 生成上传文件的唯一key + String tgtFileKey = Utility.generateFileKey(fileUploadConfig.getDirectory(), uuid, fileConform); + // 上传文件 + String fileUrl = fileManager.uploadByUrl(fileUploadConfig.getBucketName(), tgtFileKey, fileName, url, channelCode); + //重新构建fileUrl + fileUrl = rebuildFileUrl(fileUploadConfig, fileName, channelCode, bucketType, expire, tgtFileKey, fileUrl); + + File file = rebuildOssFile(fileUploadConfig, fileName, fileConform, uuid, fileUrl, Utility.getMd5(url)); + fileDao.save(file); + return file; + } + + /** + * 重新构建fileUrl + */ + private String rebuildFileUrl(FileUploadConfig fileUploadConfig, String fileName, String channelCode, String bucketType, Long expire, String tgtFileKey, String fileUrl) { + switch (BucketTypeEnum.getByCode(bucketType)) { + case PUBLIC_BUCKET: + fileUrl = this.fileManager.fetchDownloadUrl(fileUploadConfig.getBucketName(), tgtFileKey, channelCode); + break; + case PRIVATE_BUCKET: + fileUrl = this.fileManager.signUrlDownload(fileUploadConfig.getBucketName(), tgtFileKey, Objects.nonNull(expire) ? expire : SIGN_URL_DOWNLOAD_EXPIRE_SECOND , channelCode, fileName, null,false); + break; + default: + BizException.error(CodeEnum.CHANNEL_TYPE_NOT_EXIST); + } + + // 保存失败 + if (Utility.isBlank(fileUrl)) { + log.error("fileUrl is empty"); + throw new BizException(CodeEnum.FILE_UPLOAD_FAILED); + } + return fileUrl; + } + + + /** + * + * @param fileUploadConfig + * @param fileName + * @param fileConform + * @param uuid + * @param fileUrl + * @param fileMd5 + * @return + */ + private File rebuildOssFile(FileUploadConfig fileUploadConfig, String fileName, String fileConform, String uuid, + String fileUrl, String fileMd5) { + File ossFile = new File(); + ossFile.setAppChannelBucketNo(fileUploadConfig.getAppChannelBucketNo()); + ossFile.setAppCode(fileUploadConfig.getAppCode()); + ossFile.setChannelCode(fileUploadConfig.getChannelCode()); + ossFile.setBucketName(fileUploadConfig.getBucketName()); + ossFile.setDirectory(fileUploadConfig.getDirectory()); + ossFile.setStatus(FileStatusEnum.STATUS_UPLOAD_FAIL.getCode()); + ossFile.setStorageUnit(fileUploadConfig.getStorageUnit()); + ossFile.setStorageSize(fileUploadConfig.getStorageSize()); + ossFile.setFileFormat(fileConform); + ossFile.setFileUuid(uuid); + ossFile.setFileUrl(fileUrl); + ossFile.setUrlMd5(Utility.getMd5(fileUrl)); + ossFile.setStatus(FileStatusEnum.STATUS_UPLOAD_SUCCESS.getCode()); + ossFile.setFileName(fileName); + ossFile.setFileMd5(fileMd5); + return ossFile; + } + + /** + * 判断文件是否符合要求 + */ + private String isFileConform(FileUploadConfig fileUploadConfig, String fileName) { + // 文件格式判断 + String[] formats = fileUploadConfig.getFileFormat().split(FileClassEnum.COMMA.type); + + int lastIndexOf = fileName.lastIndexOf(FileClassEnum.DOT.type); + BizException + .error(lastIndexOf != CommonConstants.NOT_FOUND_INDEX_OF, CodeEnum.NOT_FILE_FORMAT); + + // 是否包含指定格式 + String fileFormat = fileName.substring(lastIndexOf + CommonConstants.ONE).toLowerCase(); + boolean contains = Arrays.asList(formats).contains(fileFormat); + BizException.error(contains, CodeEnum.FILE_FORMAT_NOT_SUPPORTED); + + return fileFormat; + } + + /** + * 构建操作日志入参 + */ + private String buildUploadParams(String appCode, String bizScene, String fileName, String url,Integer channelType) { + StringBuilder builder = new StringBuilder("appCode:"); + builder.append(appCode).append(";"); + builder.append("bizScene").append(":"); + builder.append(bizScene).append(";"); + builder.append("fileName").append(":"); + builder.append(fileName).append(";"); + builder.append("url").append(":"); + builder.append(url).append(";"); + builder.append("channelType").append(""); + builder.append(channelType).append(";"); + return builder.toString(); + } + + /** + * 构建返回对象 + */ + private ServerFileUploadResponse buildResponse(File ossFile) { + ServerFileUploadResponse response = new ServerFileUploadResponse(); + response.setUrl(ossFile.getFileUrl()); + response.setUrlMd5(ossFile.getUrlMd5()); + response.setFileKey(ossFile.getFileUuid()); + return response; + } +} 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 590b86a..c4b11f9 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 @@ -502,7 +502,7 @@ public class FileServiceImpl implements FileService { response.setFileKey(fileKey); if (Objects.nonNull(bucketTypeMap.get(file.getAppChannelBucketNo())) && BucketTypeEnum.PRIVATE_BUCKET.getCode().equals(bucketTypeMap.get(file.getAppChannelBucketNo()))) { String tgtFileKey = Utility.generateFileKey(file.getDirectory(), file.getFileUuid(), file.getFileFormat()); - response.setUrl(fileManager.signUrlDownload(file.getBucketName(), tgtFileKey, SIGN_URL_DOWNLOAD_EXPIRE_SECOND, file.getChannelCode(), file.getFileName())); + response.setUrl(fileManager.signUrlDownload(file.getBucketName(), tgtFileKey, SIGN_URL_DOWNLOAD_EXPIRE_SECOND, file.getChannelCode(), file.getFileName(), null, false)); } else { response.setUrl(fileUrl); } @@ -833,6 +833,8 @@ public class FileServiceImpl implements FileService { public List signUrlDownload(SignUrlDownloadDto dto) { log.info("signUrl download dto = {}", JsonUtil.obj2Str(dto)); Long start = System.currentTimeMillis(); + //是否代文件名称 + dto.setHasFileName(Objects.isNull(dto.getHasFileName()) ? true : dto.getHasFileName()); if (CollectionUtil.isEmpty(dto.getFileKeys())) { return Lists.newArrayList(); @@ -846,7 +848,7 @@ public class FileServiceImpl implements FileService { List httpUrlSignResList = this.buildHttpUrlSignResponse(dto); //3 构建fileKey入参对象集合 - List fileKeyResList = this.buildFileKeyResponse(dto.getFileKeys(), dto.getBizScene()); + List fileKeyResList = this.buildFileKeyResponse(dto.getFileKeys(), dto.getBizScene(), dto.getStyle(), dto.getHasFileName()); //4 1/2/3集合累加 httpUrlResList.addAll(fileKeyResList); httpUrlResList.addAll(httpUrlSignResList); @@ -857,7 +859,7 @@ public class FileServiceImpl implements FileService { /** * 构建fileKey(非http的入参)的返回对象 */ - private List buildFileKeyResponse(List fileKeys, String bizScene) { + private List buildFileKeyResponse(List fileKeys, String bizScene, String style, boolean hasFileName) { if (CollectionUtil.isEmpty(fileKeys)) { return Lists.newArrayList(); } @@ -878,13 +880,13 @@ public class FileServiceImpl implements FileService { List fileBusinessSceneList = fileBusinessSceneManager.queryByBucketNoAndScene(fileList.stream().map(File::getAppChannelBucketNo).collect(Collectors.toSet()), bizScene); Map bizSceneExpireMap = fileBusinessSceneList.stream().collect(Collectors.toMap(FileBusinessScene::getAppChannelBucketNo, FileBusinessScene::getDownloadExpiration, (x, y) -> y)); //构建返回集合 - return this.buildFileKeyRespByFile(fileList, bucketTypeMap, bizSceneExpireMap); + return this.buildFileKeyRespByFile(fileList, bucketTypeMap, bizSceneExpireMap, style, hasFileName); } /** * 通过File对象,构建返回集合 */ - private List buildFileKeyRespByFile(List fileList,Map bucketTypeMap, Map bizSceneExpireMap) { + private List buildFileKeyRespByFile(List fileList,Map bucketTypeMap, Map bizSceneExpireMap, String style, boolean hasFileName) { List responseList = fileList.stream().map(item -> { Long expire = bizSceneExpireMap.get(item.getAppChannelBucketNo()); // bucket下的key @@ -894,12 +896,12 @@ public class FileServiceImpl implements FileService { case PUBLIC_BUCKET://公有桶 - 永久链接,例如 http://xxx.png String url = this.fileManager.fetchDownloadUrl(item.getBucketName(), tgtFileKey, item.getChannelCode()); return SignUrlDownloadResponse.builder() - .signUrl(url) + .signUrl(this.buildPublicXImageProcess(url, style)) .fileKey(item.getFileUuid()) .fileName(item.getFileName()) .build(); case PRIVATE_BUCKET://私有桶 - 临时授权链接 例如 http://xxx.png?Expire=a&AccessKeyId=b&Signature=c&repsonse-content-disposition=d - String signUrl = this.fileManager.signUrlDownload(item.getBucketName(), tgtFileKey, Objects.nonNull(expire) ? expire : SIGN_URL_DOWNLOAD_EXPIRE_SECOND , item.getChannelCode(), item.getFileName()); + String signUrl = this.fileManager.signUrlDownload(item.getBucketName(), tgtFileKey, Objects.nonNull(expire) ? expire : SIGN_URL_DOWNLOAD_EXPIRE_SECOND , item.getChannelCode(), item.getFileName(), style, hasFileName); return SignUrlDownloadResponse.builder() .signUrl(UrlUtil.httpToHttps(signUrl)) .fileKey(item.getFileUuid()) @@ -925,11 +927,27 @@ public class FileServiceImpl implements FileService { return Lists.newArrayList(); } return httpUrlList.stream().map(item -> SignUrlDownloadResponse.builder() - .signUrl(item) + .signUrl(this.buildPublicXImageProcess(item, dto.getStyle())) .fileKey(item) .build()).collect(Collectors.toList()); } + /** + * 构建公开Style + */ + private String buildPublicXImageProcess(String url, String style) { + if (StringUtils.isBlank(style)) { + return url; + } + if (url.toLowerCase().contains(UrlUtil.ALIYUN) && !url.toLowerCase().contains(UrlUtil.X_OSS_PROCESS)) { + return url + UrlUtil.X_OSS_PROCESS_APPEND + style; + } + if (url.toLowerCase().contains(UrlUtil.HUAWEICLOUD) && !url.toLowerCase().contains(UrlUtil.X_IMAGE_PROCESS)) { + return url + UrlUtil.X_IMAGE_PROCESS_APPEND + style; + } + return url; + } + /** * 构建http链接(非fileKey入参)返回对象 */ @@ -945,7 +963,7 @@ public class FileServiceImpl implements FileService { if (CollectionUtil.isEmpty(map)) { return httpUrlList.stream().map(item -> SignUrlDownloadResponse.builder().fileKey(item).signUrl(item).build()).collect(Collectors.toList()); } - List responseList = this.buildFileKeyResponse(Lists.newArrayList(map.values()), null); + List responseList = this.buildFileKeyResponse(Lists.newArrayList(map.values()), null, dto.getStyle(), dto.getHasFileName()); if (CollectionUtil.isEmpty(responseList)) { return httpUrlList.stream().map(item -> SignUrlDownloadResponse.builder().fileKey(item).signUrl(item).build()).collect(Collectors.toList()); } @@ -1072,7 +1090,9 @@ public class FileServiceImpl implements FileService { Long expiration = this.buildExpiration(dto, scene); //1 调用阿里云/华为云 获取临时授权signUrl SignUrlUploadVo signUrlUpload = this.fileManager.signUrlUpload(fileUploadConfig.getBucketName(), tgtFileKey, dto.getFileName(),expiration - , fileUploadConfig.getChannelCode(), StringUtils.isNotBlank(dto.getContentType()) ? dto.getContentType() : "multipart/form-data", appChannelBucket.getBucketType()); + , fileUploadConfig.getChannelCode() + , StringUtils.isNotBlank(dto.getContentType()) ? dto.getContentType() : "multipart/form-data", appChannelBucket.getBucketType() + , Objects.isNull(dto.getHasFileName()) ? true: dto.getHasFileName() ); //2 保存File对象 this.saveOssFile(fileUploadConfig, dto.getFileName(), fileFormat, uuid, signUrlUpload.getDownloadSignUrl(), Utility.getMd5(signUrlUpload.getDownloadSignUrl()), expiration); @@ -1083,6 +1103,7 @@ public class FileServiceImpl implements FileService { .host(signUrlUpload.getHost()) .channelCode(signUrlUpload.getChannelCode()) .downloadSignUrl(UrlUtil.httpToHttps(signUrlUpload.getDownloadSignUrl())) + .fileName(dto.getFileName()) .build(); }