[REQ-2259]上传至指定文件

This commit is contained in:
xudawei 2024-07-01 16:26:38 +08:00
parent 3d26392123
commit 0af4e47039
13 changed files with 339 additions and 20 deletions

View File

@ -21,6 +21,8 @@ import cn.axzo.oss.http.model.FindFileUrlRequest;
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.ServerFileCopyToDictRequest;
import cn.axzo.oss.http.model.ServerFileCopyToDictResponse;
import cn.axzo.oss.http.model.ServerFileDeleteRequest;
import cn.axzo.oss.http.model.ServerFileUploadByUrlRequest;
import cn.axzo.oss.http.model.ServerFileUploadRequest;
@ -28,6 +30,7 @@ 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.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;
@ -35,6 +38,7 @@ 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.FileCopyToDictService;
import cn.axzo.oss.service.api.FileService;
import cn.azxo.framework.common.model.CommonResponse;
import cn.hutool.json.JSONUtil;
@ -68,6 +72,9 @@ public class ServerFileController implements ServerFileServiceApi {
@Autowired
private FileByUrlService fileByUrlService;
@Autowired
private FileCopyToDictService fileCopyToDictService;
@Autowired
private HttpServletRequest httpServletRequest;
@ -214,4 +221,15 @@ public class ServerFileController implements ServerFileServiceApi {
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));
}
/**
* 通过URL上传文件
* @param request
* @return
*/
public CommonResponse<ServerFileCopyToDictResponse> copyToDict(@Valid @RequestBody ServerFileCopyToDictRequest request) {
ServerFileCopyToDictDto dto = BeanConvertUtil.copyBean(request, ServerFileCopyToDictDto.class);
String targetFileKey = fileCopyToDictService.copyToDict(dto);
return CommonResponse.success(ServerFileCopyToDictResponse.builder().targetFileKey(targetFileKey).build());
}
}

View File

@ -13,6 +13,8 @@ import cn.axzo.oss.http.model.FindFileUrlRequest;
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.ServerFileCopyToDictRequest;
import cn.axzo.oss.http.model.ServerFileCopyToDictResponse;
import cn.axzo.oss.http.model.ServerFileDeleteRequest;
import cn.axzo.oss.http.model.ServerFileUploadByUrlRequest;
import cn.axzo.oss.http.model.ServerFileUploadRequest;
@ -107,4 +109,12 @@ public interface ServerFileServiceApi {
@RequestMapping(value = "/api/v1/server/uploadByUrl", method = RequestMethod.POST)
CommonResponse<ServerFileUploadResponse> uploadByUrl(ServerFileUploadByUrlRequest request);
/**
* 通过URL上传文件
* @param request
* @return
*/
@RequestMapping(value = "/api/server/copyToDict", method = RequestMethod.POST)
CommonResponse<ServerFileCopyToDictResponse> copyToDict(ServerFileCopyToDictRequest request);
}

View File

@ -0,0 +1,39 @@
package cn.axzo.oss.http.model;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 复制至指定目录下
*
* @author xudawei
* @date 2024-07-01
*/
@Data
public class ServerFileCopyToDictRequest {
@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")
@NotNull(message = "fileKey must not be null")
private String fileKey;
@NotNull(message = "fileKey must not be null")
private String targetDict;
private Integer channelCode;
/**
* 图片样式比如x-oss-process=image/auto-orient,1/resize,p_50/quality,q_30
*/
private String style;
/**
* 最大文件大小
*/
private Long maxFileSize;
}

View File

@ -0,0 +1,22 @@
package cn.axzo.oss.http.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 复制至指定目录下
*
* @author xudawei
* @date 2024-07-01
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ServerFileCopyToDictResponse {
private String targetFileKey;
}

View File

@ -51,4 +51,9 @@ public interface HuaWeiCloudService {
* 元数据
*/
ObjectMetadata getObjectMeta(String bucketName, String key, String url);
/**
* 元数据
*/
boolean copyToDict(String bucketName, String key, String targetBucketName, String targetKey);
}

View File

@ -15,6 +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.DeleteObjectResult;
import com.obs.services.model.DownloadFileRequest;
import com.obs.services.model.DownloadFileResult;
@ -468,7 +469,28 @@ public class HuaWeiCloudServiceImpl implements HuaWeiCloudService {
return new ObjectMetadata();
} catch (Exception e) {
log.warn("huaweicloud-getObjectMeta-exception, bucketName:{}, key:{}", bucketName, key, e);
return new ObjectMetadata();
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(0L);
return objectMetadata;
}
}
/**
* 元数据
*/
@Override
public boolean copyToDict(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;
} catch (Exception e) {
log.warn("huaweicloud-copyToDict-exception, bucketName:{}, key:{}", bucketName, key, e);
return false;
}
}

View File

@ -3,6 +3,7 @@ package cn.axzo.oss.manager.api;
import cn.axzo.oss.manager.api.dto.PartETag;
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 org.springframework.web.multipart.MultipartFile;
@ -87,4 +88,11 @@ public interface FileManager {
*/
String uploadByUrl(String bulkName, String keyPath, String fileName, String url,
String channelCode);
SignUrlDownloadResponse fetchDownloadUrl(String bucketType, String bucketName, String bucketKey, String channelCode, Long expire, String fileName, String style, boolean hasFileName);
/**
* 构建公开Style
*/
String buildPublicXImageProcess(String url, String style);
}

View File

@ -0,0 +1,42 @@
package cn.axzo.oss.manager.api.dto.request;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 复制至指定目录下
*
* @author xudawei
* @date 2024-07-01
*/
@Data
public class ServerFileCopyToDictDto {
@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 = "fileKey must not be null")
private String fileKey;
@NotNull(message = "targetDict must not be null")
private String targetDict;
private Integer channelCode;
/**
* 图片样式比如x-oss-process=image/auto-orient,1/resize,p_50/quality,q_30
*/
private String style;
/**
* 最大文件大小
*/
private Long maxFileSize;
}

View File

@ -14,6 +14,7 @@ import cn.axzo.oss.manager.api.FileManager;
import cn.axzo.oss.manager.api.dto.PartETag;
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.aliyun.oss.model.SimplifiedObjectMeta;
import com.obs.services.model.ObjectMetadata;
@ -22,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@ -29,6 +31,7 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @Author admin
@ -49,6 +52,9 @@ public class FileManagerImpl implements FileManager {
private final String HUAWEI_CLOUD_SIGNURL_UPLOAD_HOST_KEY = "Host";
private final String HUAWEI_CLOUD_SIGNURL_UPLOAD_CONTENT_TYPE_KEY = "Content-Type";
@Value("${sign.url.download.expire.second:2000}")
private Long SIGN_URL_DOWNLOAD_EXPIRE_SECOND;
/**
* 删除文件
*
@ -290,10 +296,50 @@ public class FileManagerImpl implements FileManager {
BizException.error(CodeEnum.CHANNEL_TYPE_NOT_EXIST);
}
} catch (Exception e) {
log.error("通过url上传至云 exception,bulkName:{}, key:{}, fileName:{}, url:{},channelCode:{}"
log.warn("通过url上传至云 exception,bulkName:{}, key:{}, fileName:{}, url:{},channelCode:{}"
, bulkName, keyPath, fileName, url, channelCode, e);
return StringUtils.EMPTY;
}
return StringUtils.EMPTY;
}
@Override
public SignUrlDownloadResponse fetchDownloadUrl(String bucketType, String bucketName, String bucketKey , String channelCode, Long expire, String fileName, String style, boolean hasFileName) {
switch (BucketTypeEnum.getByCode(bucketType)) {
case PUBLIC_BUCKET://公有桶 - 永久链接例如 http://xxx.png
String url = this.fetchDownloadUrl(bucketName, bucketKey, channelCode);
return SignUrlDownloadResponse.builder()
.signUrl(this.buildPublicXImageProcess(url, style))
.fileName(fileName)
.build();
case PRIVATE_BUCKET://私有桶 - 临时授权链接 例如 http://xxx.png?Expire=a&AccessKeyId=b&Signature=c&repsonse-content-disposition=d
String signUrl = this.signUrlDownload(bucketName, bucketKey, Objects.nonNull(expire) ? expire : SIGN_URL_DOWNLOAD_EXPIRE_SECOND , channelCode, fileName, style, hasFileName);
return SignUrlDownloadResponse.builder()
.signUrl(UrlUtil.httpToHttps(signUrl))
.fileName(fileName)
.build();
default:
BizException.error(CodeEnum.CHANNEL_TYPE_NOT_EXIST);
}
return SignUrlDownloadResponse.builder().build();
}
/**
* 构建公开Style
*/
@Override
public 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;
}
}

View File

@ -0,0 +1,14 @@
package cn.axzo.oss.service.api;
import cn.axzo.oss.manager.api.dto.request.ServerFileCopyToDictDto;
/**
* @author xudawei@axzo.cn
* @date 2024/7/1
* @description TODO
*/
public interface FileCopyToDictService {
String copyToDict(ServerFileCopyToDictDto dto);
}

View File

@ -1,6 +1,8 @@
package cn.axzo.oss.service.api;
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.FindFileKeyDto;
import cn.axzo.oss.manager.api.dto.request.FindFileUrlDto;
@ -23,6 +25,7 @@ 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.hutool.core.lang.Pair;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@ -92,4 +95,14 @@ public interface FileService {
* 元数据
*/
ManaGetObjectMetaResponse getObjectMeta(String bucketName, String key, String url,String channelCode);
/**
* 获取桶配置
*/
FileUploadConfig getFileUploadConfig(String appCode, String bizScene, Integer channelType);
/**
* 获取桶基础与配置
*/
Pair<AppChannelBucket, FileUploadConfig> channelBucketAndConfig(String appCode, String bizScene, Integer channelType);
}

View File

@ -0,0 +1,73 @@
package cn.axzo.oss.service.impl;
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.FileUploadConfig;
import cn.axzo.oss.dal.repository.FileDao;
import cn.axzo.oss.manager.api.FileManager;
import cn.axzo.oss.manager.api.dto.request.ServerFileCopyToDictDto;
import cn.axzo.oss.manager.api.dto.response.ManaGetObjectMetaResponse;
import cn.axzo.oss.manager.api.dto.response.SignUrlDownloadResponse;
import cn.axzo.oss.service.api.FileCopyToDictService;
import cn.axzo.oss.service.api.FileService;
import cn.hutool.core.lang.Pair;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Service;
import java.util.Objects;
/**
* @Author admin
* @Description
* @Date 2021/7/28 22:48
* @Version 0.0.1
**/
@Service
@RefreshScope
@Slf4j
public class FileCopyToDictServiceImpl implements FileCopyToDictService {
@Autowired
private FileService fileService;
@Autowired
private FileManager fileManager;
@Autowired
private FileDao fileDao;
@Value("${sign.url.download.expire.second:2000}")
private Long SIGN_URL_DOWNLOAD_EXPIRE_SECOND;
@Override
public String copyToDict(ServerFileCopyToDictDto dto) {
//1 获取桶基础与配置
Pair<AppChannelBucket, FileUploadConfig> pair = this.fileService.channelBucketAndConfig(dto.getAppCode(), dto.getBizScene(), dto.getChannelCode());
AppChannelBucket appChannelBucket = pair.getKey();
FileUploadConfig fileUploadConfig = pair.getValue();
//2 获取文件信息
File byFileUuid = this.fileDao.getByFileUuid(dto.getFileKey());
String sourceFileKey = Utility.generateFileKey(byFileUuid.getDirectory(), dto.getFileKey(), byFileUuid.getFileFormat());
String targetFileKey = Utility.generateFileKey(fileUploadConfig.getDirectory() + "_" + dto.getTargetDict(), dto.getFileKey(), byFileUuid.getFileFormat());
//3 获取下载文件url(支持:公有桶/私有桶)
SignUrlDownloadResponse downloadResponse = this.fileManager.fetchDownloadUrl(appChannelBucket.getBucketType(), fileUploadConfig.getBucketName(), sourceFileKey
, fileUploadConfig.getChannelCode(), SIGN_URL_DOWNLOAD_EXPIRE_SECOND, byFileUuid.getFileName(), null, false);
//4 获取文件元信息(此时主要文件大小)
ManaGetObjectMetaResponse objectMeta = fileService.getObjectMeta(appChannelBucket.getBucketName(), sourceFileKey, downloadResponse.getSignUrl(), appChannelBucket.getChannelCode());
//文件大小没有达到maxFileSize则不用裁剪
if (Objects.nonNull(dto.getMaxFileSize()) && !Objects.equals(dto.getMaxFileSize(), 0) && !Objects.equals(objectMeta.getSize(), 0)&& objectMeta.getSize() > dto.getMaxFileSize()) {
//5 重新获取下载文件url(支持:公有桶/私有桶)
downloadResponse = this.fileManager.fetchDownloadUrl(appChannelBucket.getBucketType(), fileUploadConfig.getBucketName(), sourceFileKey
, fileUploadConfig.getChannelCode(), SIGN_URL_DOWNLOAD_EXPIRE_SECOND, byFileUuid.getFileName(), dto.getStyle(), false);
}
//6 上传至指定目录
this.fileManager.uploadByUrl(fileUploadConfig.getBucketName(), targetFileKey, byFileUuid.getFileName(), downloadResponse.getSignUrl(), fileUploadConfig.getChannelCode());
return targetFileKey;
}
}

View File

@ -55,6 +55,7 @@ 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.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
@ -302,7 +303,8 @@ public class FileServiceImpl implements FileService {
return setFileDownloadResponse(file, fileStream);
}
private FileUploadConfig getFileUploadConfig(String appCode, String bizScene, Integer channelType) {
@Override
public FileUploadConfig getFileUploadConfig(String appCode, String bizScene, Integer channelType) {
// 检查appCode
checkAppCode(appCode);
@ -319,6 +321,25 @@ public class FileServiceImpl implements FileService {
return fileUploadConfig;
}
@Override
public Pair<AppChannelBucket, FileUploadConfig> channelBucketAndConfig(String appCode, String bizScene, Integer channelType) {
// 检查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());
return Pair.of(appChannelBucket, fileUploadConfig);
}
private File completeFile(FileUploadConfig fileUploadConfig, String fileName, String tgtFileKey, String uploadId, List<PartETag> partETags) {
// 文件合并
String fileUrl = fileManager.multipartUploadComplete(fileUploadConfig.getBucketName(), tgtFileKey, uploadId, partETags);
@ -896,7 +917,7 @@ 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(this.buildPublicXImageProcess(url, style))
.signUrl(this.fileManager.buildPublicXImageProcess(url, style))
.fileKey(item.getFileUuid())
.fileName(item.getFileName())
.build();
@ -927,26 +948,12 @@ public class FileServiceImpl implements FileService {
return Lists.newArrayList();
}
return httpUrlList.stream().map(item -> SignUrlDownloadResponse.builder()
.signUrl(this.buildPublicXImageProcess(item, dto.getStyle()))
.signUrl(this.fileManager.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入参)返回对象