Pre Merge pull request !504 from 秋辞未寒/dev

This commit is contained in:
秋辞未寒 2024-04-24 05:15:40 +00:00 committed by Gitee
commit 94e97d9f5b
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
12 changed files with 747 additions and 3 deletions

View File

@ -37,4 +37,8 @@ public interface OssConstant {
*/
String IS_HTTPS = "Y";
/**
* 分片上传信息 cacheKey
*/
String PART_UPLOAD_INFO_CACHE_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss:part_upload_info:";
}

View File

@ -7,6 +7,9 @@ import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.oss.constant.OssConstant;
import org.dromara.common.oss.entity.CreatePartUploadResult;
import org.dromara.common.oss.entity.PartInfo;
import org.dromara.common.oss.entity.PartUploadResult;
import org.dromara.common.oss.entity.UploadResult;
import org.dromara.common.oss.enumd.AccessPolicyType;
import org.dromara.common.oss.enumd.PolicyType;
@ -19,8 +22,7 @@ import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.*;
@ -35,6 +37,9 @@ import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* S3 存储协议 所有兼容S3协议的云厂商均支持
@ -233,6 +238,212 @@ public class OssClient {
}
}
/**
* 创建分片任务
*
* @param key Amazon S3 中的对象键
* @return CreatePartUploadResult 创建上传分片任务返回体包含上传后的文件信息
* @throws OssException 如果创建失败抛出自定义异常
*/
public CreatePartUploadResult createPartUpload(String key) {
try {
// 创建分片上传
CompletableFuture<CreateMultipartUploadResponse> createPartUploadFuture = client.createMultipartUpload(builder -> builder.bucket(properties.getBucketName())
.key(key)
.acl(getAccessPolicy().getObjectCannedACL())
.build());
// 等待创建分片上传任务完成
CreateMultipartUploadResponse createPartUploadResult = createPartUploadFuture.join();
// 获取分片上传任务 uploadId
String uploadId = createPartUploadResult.uploadId();
return CreatePartUploadResult.builder().uploadId(uploadId).url(getUrl() + StringUtils.SLASH + key).filename(key).build();
} catch (Exception e) {
throw new OssException("创建分片上传任务失败,请检查配置信息:[" + e.getMessage() + "]");
}
}
/**
* 创建分片任务
*
* @param suffix 对象键的后缀
* @return CreatePartUploadResult 创建上传分片任务返回体包含上传后的文件信息
* @throws OssException 如果创建失败抛出自定义异常
*/
public CreatePartUploadResult createPartUploadSuffix(String suffix) {
return createPartUpload(getPath(properties.getPrefix(), suffix));
}
/**
* 分片文件上传
*
* @param data 文件字节数组
* @param key Amazon S3 中的对象键
* @param uploadId oss分片上传id
* @param partNumber 分片序号
* @return PartUploadResult 分片上传结果包含上传后的分片文件信息
* @throws OssException 如果上传失败抛出自定义异常
*/
public PartUploadResult partUpload(byte[] data, String key, String uploadId, Integer partNumber) {
try {
// 创建异步请求体
AsyncRequestBody requestBody = AsyncRequestBody.fromBytes(data);
// 使用 s3client 进行分片上传
CompletableFuture<UploadPartResponse> partUploadFuture = client.uploadPart(builder -> builder.bucket(properties.getBucketName())
.key(key)
.uploadId(uploadId)
.partNumber(partNumber)
.build(), requestBody);
// 等待文件上传操作完成
UploadPartResponse partUploadResult = partUploadFuture.join();
String eTag = partUploadResult.eTag();
// 提取上传结果中的 ETag并构建一个自定义的 PartUploadResult 对象
return PartUploadResult.builder().uploadId(uploadId).partNumber(partNumber).partSize((long) data.length).eTag(eTag).build();
} catch (Exception e) {
throw new OssException("上传分片文件失败,请检查配置信息:[" + e.getMessage() + "]");
}
}
/**
* 分片文件上传
*
* @param inputStream 要上传的输入流
* @param key Amazon S3 中的对象键
* @param uploadId oss分片上传id
* @param partNumber 分片序号
* @param partSize 分片大小
* @return PartUploadResult 分片上传结果包含上传后的分片文件信息
* @throws OssException 如果上传失败抛出自定义异常
*/
public PartUploadResult partUpload(InputStream inputStream, String key, String uploadId, Integer partNumber, Long partSize) {
if (!(inputStream instanceof ByteArrayInputStream)) {
inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
}
try {
// 创建异步请求体length如果为空会报错
BlockingInputStreamAsyncRequestBody requestBody = AsyncRequestBody.forBlockingInputStream(partSize);
// 使用 s3client 进行分片上传
CompletableFuture<UploadPartResponse> partUploadFuture = client.uploadPart(builder -> builder.bucket(properties.getBucketName())
.key(key)
.uploadId(uploadId)
.partNumber(partNumber)
.build(), requestBody);
// 将输入流写入请求体
requestBody.writeInputStream(inputStream);
// 等待文件上传操作完成
UploadPartResponse partUploadResult = partUploadFuture.join();
String eTag = partUploadResult.eTag();
// 提取上传结果中的 ETag并构建一个自定义的 PartUploadResult 对象
return PartUploadResult.builder().uploadId(uploadId).partNumber(partNumber).partSize(partSize).eTag(eTag).build();
} catch (Exception e) {
throw new OssException("上传分片文件失败,请检查配置信息:[" + e.getMessage() + "]");
}
}
/**
* 终止分片上传任务
*
* @param uploadId 分片上传任务ID
* @param key 文件在 Amazon S3 中的对象键
* @return boolean 终止分片上传任务结果
* @throws OssException 如果终止分片上传失败抛出自定义异常
*/
public boolean abortPartUpload(String uploadId, String key) {
try {
// 使用 s3client 请求终止分片上传任务
CompletableFuture<AbortMultipartUploadResponse> abortPartUploadFuture = client.abortMultipartUpload(builder -> builder.bucket(properties.getBucketName())
.uploadId(uploadId)
.key(key)
.build());
// 等待终止分片上传任务请求响应
AbortMultipartUploadResponse abortPartUploadResult = abortPartUploadFuture.join();
// 没有异常即成功
return true;
} catch (Exception e) {
// throw new OssException("终止分片上传任务失败,请检查配置信息:[" + e.getMessage() + "]");
return false;
}
}
/**
* 列出已经上传的分片
*
* @param uploadId 分片上传任务ID
* @param key 文件在 Amazon S3 中的对象键
* @return List<Part> 已经上传的分片信息
*/
public List<Part> listParts(String uploadId, String key) {
// 使用 s3client 请求获取已经上传的分片信息
CompletableFuture<ListPartsResponse> listPartsFuture = client.listParts(builder -> builder.bucket(properties.getBucketName())
.uploadId(uploadId)
.key(key)
.build());
// 等待获取已经上传的分片信息请求响应
ListPartsResponse listPartsResult = listPartsFuture.join();
return listPartsResult.parts();
}
/**
* 完成分片上传 - 合并分片
*
* @param uploadId 分片上传任务ID
* @param key 文件在 Amazon S3 中的对象键
* @param completedParts 已经上传的分片信息
* @return UploadResult 包含上传后的文件信息
*/
public UploadResult completePartUpload(String uploadId, String key, Collection<CompletedPart> completedParts) {
// 使用 s3client 请求完成分片上传任务
CompletableFuture<CompleteMultipartUploadResponse> completePartUploadFuture = client.completeMultipartUpload(builder -> builder.bucket(properties.getBucketName())
.uploadId(uploadId)
.key(key)
.multipartUpload(CompletedMultipartUpload.builder().parts(completedParts).build())
.build());
// 等待完成分片上传任务请求响应
CompleteMultipartUploadResponse completePartUploadResult = completePartUploadFuture.join();
// 提取上传结果中的 ETag并构建一个自定义的 UploadResult 对象
return UploadResult.builder().url(getUrl() + "/" + key).filename(key).eTag(completePartUploadResult.eTag()).build();
}
/**
* 完成分片上传 - 合并分片
*
* @param uploadId 分片上传任务ID
* @param key 文件在 Amazon S3 中的对象键
* @param partInfoList 已经上传的分片信息
* @return UploadResult 包含上传后的文件信息
*/
public UploadResult completePartUpload(String uploadId, String key, List<PartInfo> partInfoList) {
List<CompletedPart> completedParts = partInfoList.stream()
.map(partInfo -> CompletedPart.builder()
.partNumber(partInfo.getPartNumber())
.eTag(partInfo.getETag())
.build())
.toList();
return completePartUpload(uploadId, key, completedParts);
}
/**
* 完成分片上传 - 合并分片根据已上传的分片结果合并
*
* @param uploadId 分片上传任务ID
* @param key 文件在 Amazon S3 中的对象键
* @return UploadResult 包含上传后的文件信息
*/
public UploadResult completePartUpload(String uploadId, String key) {
// 获取已上传的文件分片列表
List<Part> parts = listParts(uploadId, key);
// 构建 CompletedPart 列表
List<CompletedPart> completedParts = parts.stream()
.map(part -> CompletedPart.builder().partNumber(part.partNumber()).eTag(part.eTag()).build())
.toList();
// 合并分片并返回结果
return completePartUpload(uploadId, key, completedParts);
}
/**
* 下载文件从 Amazon S3 到临时目录
*

View File

@ -0,0 +1,30 @@
package org.dromara.common.oss.entity;
import lombok.Builder;
import lombok.Data;
/**
* 创建上传分片任务返回体
*
* @author SunnyDeer0911
*/
@Data
@Builder
public class CreatePartUploadResult {
/**
* 文件路径
*/
private String url;
/**
* 分片上传ID
*/
private String uploadId;
/**
* 文件名
*/
private String filename;
}

View File

@ -0,0 +1,32 @@
package org.dromara.common.oss.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* 分片信息对象
*
* @author SunnyDeer0911
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class PartInfo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 分片序号
*/
private Integer partNumber;
/**
* 已上传分片的实体标记用来校验文件
*/
private String eTag;
}

View File

@ -0,0 +1,81 @@
package org.dromara.common.oss.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 分片上传信息对象
* TODO 用于创建分片上传任务时数据落库之前存放分片上传信息数据到Redis
*
* @author SunnyDeer0911
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class PartUploadInfo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 文件上传ID创建分片上传任务时S3 OSS返回的唯一标识
*/
private String uploadId;
/**
* 文件名
*/
private String fileName;
/**
* 原文件名
*/
private String originalName;
/**
* 文件后缀名
*/
private String fileSuffix;
/**
* URL地址
*/
private String url;
/**
* 文件大小
*/
private Long fileSize;
/**
* 服务商
*/
private String service;
/**
* 分片大小
*/
private Long partSize;
/**
* 分片数量
*/
private Long totalParts;
/**
* 是否需要合并
*/
private boolean needMerge;
/**
* 已完成上传的分片列表
*/
List<PartInfo> partInfoList;
}

View File

@ -0,0 +1,35 @@
package org.dromara.common.oss.entity;
import lombok.Builder;
import lombok.Data;
/**
* 分片上传返回体
*
* @author SunnyDeer0911
*/
@Data
@Builder
public class PartUploadResult {
/**
* 分片上传ID
*/
private String uploadId;
/**
* 分片序号
*/
private Integer partNumber;
/***
* 分片大小
*/
private Long partSize;
/**
* 已上传对象的实体标记用来校验文件
*/
private String eTag;
}

View File

@ -0,0 +1,101 @@
package org.dromara.common.oss.utils;
import cn.hutool.core.collection.CollUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.oss.constant.OssConstant;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.entity.PartUploadInfo;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.common.redis.utils.RedisUtils;
import java.util.Collection;
/**
* 分片上传缓存助手
*
* @author SunnyDeer0911
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PartUploadCacheHelper {
/**
* 获取分片上传信息cacheKey
* @param uploadId 文件分片上传ID
* @return 分片上传信息cacheKey
*/
public static String getPartUploadInfoCacheKey(String uploadId){
if (StringUtils.isBlank(uploadId)) {
return null;
}
return OssConstant.PART_UPLOAD_INFO_CACHE_KEY + uploadId;
}
/**
* 从缓存中获取分片上传信息对象
* @param uploadId 文件分片上传ID
* @return 获取分片上传信息对象
*/
public static PartUploadInfo getCache(String uploadId){
String cacheKey = getPartUploadInfoCacheKey(uploadId);
if (cacheKey==null) {
return null;
}
return RedisUtils.getCacheObject(cacheKey);
}
/**
* 将分片上传信息对象放入缓存
* @param partUploadInfo 分片上传信息对象
*/
public static void putCache(PartUploadInfo partUploadInfo){
String cacheKey = getPartUploadInfoCacheKey(partUploadInfo.getUploadId());
if (cacheKey==null) {
return;
}
RedisUtils.setCacheObject(cacheKey, partUploadInfo);
}
/**
* 移除分片上传信息对象缓存
* @param uploadId 文件分片上传ID
*/
public static void removeCache(String uploadId){
String cacheKey = getPartUploadInfoCacheKey(uploadId);
if (cacheKey==null) {
return;
}
removeCacheByKey(cacheKey);
}
/**
* 移除分片上传信息对象缓存
* @param cacheKey 缓存key
*/
private static void removeCacheByKey(String cacheKey){
PartUploadInfo cache = RedisUtils.getCacheObject(cacheKey);
if (cache==null) {
return;
}
// 如果未合并则调用OssClient终止分片上传任务的方法让OSS删除已上传的分片
if (!cache.isNeedMerge() && CollUtil.isNotEmpty(cache.getPartInfoList())) {
OssClient instance = OssFactory.instance(cache.getService());
// 终止分片上传任务
instance.abortPartUpload(cache.getUploadId(),cache.getFileName());
}
RedisUtils.deleteObject(cacheKey);
}
/**
* 移除所有分片上传信息对象缓存
*/
public static void removeAllCache(){
Collection<String> removeKeys = RedisUtils.keys(OssConstant.PART_UPLOAD_INFO_CACHE_KEY + "*");
if (CollUtil.isEmpty(removeKeys)) {
return;
}
removeKeys.forEach(PartUploadCacheHelper::removeCacheByKey);
}
}

View File

@ -11,6 +11,8 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.bo.SysOssBo;
import org.dromara.system.domain.bo.SysOssPartUploadBo;
import org.dromara.system.domain.vo.SysOssPartUploadVo;
import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
@ -105,4 +107,20 @@ public class SysOssController extends BaseController {
return toAjax(ossService.deleteWithValidByIds(List.of(ossIds), true));
}
/**
* 上传OSS对象分片文件
*
* @param file 分片文件
* @param bo OSS分片上传业务对象
* @return R 上传结果
*/
@SaCheckPermission("system:oss:upload")
@Log(title = "OSS对象存储-分片上传", businessType = BusinessType.INSERT)
@PostMapping(value = "/part/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysOssPartUploadVo> partUpload(@RequestPart("file") MultipartFile file, @Validated SysOssPartUploadBo bo) {
if (ObjectUtil.isNull(file)) {
return R.fail("上传文件不能为空");
}
return R.ok(ossService.partUpload(file, bo));
}
}

View File

@ -0,0 +1,64 @@
package org.dromara.system.domain.bo;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* OSS分片上传业务对象
*
* @author SunnyDeer0911
*/
@Data
public class SysOssPartUploadBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 文件上传ID创建分片上传任务时S3 OSS返回的唯一标识
* TODO 上传第一片分片时允许为空后续需要从第一次分片上传的响应中获取回填
*/
private String uploadId;
/**
* 文件名
*/
@NotBlank(message = "文件名不能为空")
private String fileName;
/**
* 文件大小文件的总大小
*/
@NotNull(message = "文件大小不能为空")
private Long fileSize;
/**
* 分片序号*分片序号从1开始
*/
@NotNull(message = "分片序号不能为空")
@Min(value = 1, message = "分片序号不能小于1")
private Integer partNumber;
/**
* 分片大小
*/
// @NotNull(message = "分片大小不能为空")
private Long partSize;
/**
* 分片数量
*/
// @NotNull(message = "分片数量不能为空")
private Long totalParts;
/**
* 是否需要合并
*/
@NotNull(message = "是否需要合并不能为空")
private Boolean needMerge;
}

View File

@ -0,0 +1,47 @@
package org.dromara.system.domain.vo;
import lombok.Data;
import org.dromara.common.oss.entity.PartInfo;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 分片上传对象信息VO
*
* @author SunnyDeer0911
*/
@Data
public class SysOssPartUploadVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 分片上传ID
*/
private String uploadId;
/**
* URL地址
*/
private String url;
/**
* 已完成上传的分片列表
*/
List<PartInfo> partInfoList;
/**
* 合并完成
*/
private Boolean mergeCompleted;
/**
* 分片上传ID - 仅在文件成功合并数据落库时才会生成
* @see this#mergeCompleted 分片文件合并状态
*/
private Long ossId;
}

View File

@ -3,6 +3,8 @@ package org.dromara.system.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.bo.SysOssBo;
import org.dromara.system.domain.bo.SysOssPartUploadBo;
import org.dromara.system.domain.vo.SysOssPartUploadVo;
import org.dromara.system.domain.vo.SysOssVo;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
@ -77,4 +79,13 @@ public interface ISysOssService {
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 上传文件分片到对象存储服务
*
* @param file 要上传的文件对象
* @param bo OSS分片上传业务对象
* @return 上传成功后的 SysOssPartUploadVo 对象包含文件分片上传结果信息
*/
SysOssPartUploadVo partUpload(MultipartFile file, SysOssPartUploadBo bo);
}

View File

@ -19,11 +19,14 @@ import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.entity.UploadResult;
import org.dromara.common.oss.entity.*;
import org.dromara.common.oss.enumd.AccessPolicyType;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.common.oss.utils.PartUploadCacheHelper;
import org.dromara.system.domain.SysOss;
import org.dromara.system.domain.bo.SysOssBo;
import org.dromara.system.domain.bo.SysOssPartUploadBo;
import org.dromara.system.domain.vo.SysOssPartUploadVo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.mapper.SysOssMapper;
import org.dromara.system.service.ISysOssService;
@ -237,6 +240,113 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
return baseMapper.deleteBatchIds(ids) > 0;
}
@Override
public SysOssPartUploadVo partUpload(MultipartFile file, SysOssPartUploadBo bo) {
try {
String uploadId = bo.getUploadId();
// 如果 uploadId 为空则说明是新建上传分片
if (StringUtils.isBlank(uploadId)) {
return createPartUpload(file, bo);
}
// uploadId不为空从缓存中获取分片上传信息
PartUploadInfo partUploadInfo = PartUploadCacheHelper.getCache(uploadId);
if (ObjectUtil.isNull(partUploadInfo)) {
throw new ServiceException("未找到分片上传信息!");
}
// TODO 是否需要合并分片
partUploadInfo.setNeedMerge(bo.getNeedMerge());
OssClient storage = OssFactory.instance(partUploadInfo.getService());
// 上传分片并返回结果
return partUpload(storage, partUploadInfo, file, bo.getPartNumber());
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
}
/**
* 创建并上传分片
*
* @param file 需要进行上传的分片文件
* @param bo OSS分片上传业务对象
* @return 分片上传对象信息VO
*/
private SysOssPartUploadVo createPartUpload(MultipartFile file, SysOssPartUploadBo bo) throws IOException {
// 文件大小
Long fileSize = bo.getFileSize();
// 分片大小
Long partSize = bo.getPartSize();
// 分片数量
Long totalParts = bo.getTotalParts();
// 文件名
String originalFileName = bo.getFileName();
// 从文件名中获取文件后缀
String suffix = StringUtils.substring(originalFileName, originalFileName.lastIndexOf("."), originalFileName.length());
OssClient storage = OssFactory.instance();
CreatePartUploadResult partUpload = storage.createPartUploadSuffix(suffix);
// 构建分片上传信息对象
PartUploadInfo partUploadInfo = new PartUploadInfo();
partUploadInfo.setUploadId(partUpload.getUploadId());
partUploadInfo.setFileName(partUpload.getFilename());
partUploadInfo.setOriginalName(originalFileName);
partUploadInfo.setFileSuffix(suffix);
partUploadInfo.setUrl(partUpload.getUrl());
partUploadInfo.setFileSize(fileSize);
partUploadInfo.setService(storage.getConfigKey());
partUploadInfo.setPartSize(partSize);
partUploadInfo.setTotalParts(totalParts);
partUploadInfo.setPartInfoList(new ArrayList<>());
// TODO 是否需要合并分片
partUploadInfo.setNeedMerge(bo.getNeedMerge());
// 上传分片并返回结果
return partUpload(storage, partUploadInfo, file, bo.getPartNumber());
}
/**
* 上传分片
*
* @param storage OSS客户端
* @param partUploadInfo 分片上传信息对象
* @param file 需要进行上传的分片文件
* @param partNumber 分片序号
* @return 分片上传对象信息VO
*/
private SysOssPartUploadVo partUpload(OssClient storage, PartUploadInfo partUploadInfo, MultipartFile file, Integer partNumber) throws IOException {
// 文件上传ID
String uploadId = partUploadInfo.getUploadId();
// 构建分片上传对象信息VO
SysOssPartUploadVo sysOssPartUploadVo = new SysOssPartUploadVo();
sysOssPartUploadVo.setUploadId(uploadId);
sysOssPartUploadVo.setUrl(partUploadInfo.getUrl());
sysOssPartUploadVo.setPartInfoList(partUploadInfo.getPartInfoList());
sysOssPartUploadVo.setMergeCompleted(partUploadInfo.isNeedMerge());
// 上传分片
PartUploadResult partUploadResult = storage.partUpload(file.getInputStream(), partUploadInfo.getFileName(), uploadId, partNumber, file.getSize());
// 将完成上传的分片信息放入集合中
partUploadInfo.getPartInfoList().add(new PartInfo(partUploadResult.getPartNumber(), partUploadResult.getETag()));
// 分片上传信息放入缓存
PartUploadCacheHelper.putCache(partUploadInfo);
// 检查是否需要合并 - 已经完成所有上传时操作
if (partUploadInfo.isNeedMerge()) {
// 如果已经完成最后一片的上传则进行合并
UploadResult uploadResult = storage.completePartUpload(uploadId, partUploadInfo.getFileName(), partUploadInfo.getPartInfoList());
// 不报错即合并成功删除缓存中的分片上传信息
PartUploadCacheHelper.removeCache(uploadId);
// 数据落库
SysOss oss = new SysOss();
oss.setFileSuffix(partUploadInfo.getFileSuffix());
oss.setOriginalName(partUploadInfo.getOriginalName());
oss.setService(storage.getConfigKey());
oss.setFileName(partUploadInfo.getFileName());
oss.setUrl(partUploadInfo.getUrl());
baseMapper.insert(oss);
// 回填对象存储ID
sysOssPartUploadVo.setOssId(oss.getOssId());
}
return sysOssPartUploadVo;
}
/**
* 桶类型为 private 的URL 修改为临时URL时长为120s
*