修改:多文件,分片上传接口修改。
This commit is contained in:
@@ -96,10 +96,6 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
@Value("${fileSystem.chunkBucket:spdm}")
|
||||
private String chunkBucket;
|
||||
|
||||
// 路径待确定 待配置及初始化
|
||||
@Value("${fileSystem.chunkBasePath:/chunkBase}")
|
||||
private String chunkBasePath;
|
||||
|
||||
@Autowired
|
||||
private IFileMetadataInfoService fileMetadataInfoService;
|
||||
|
||||
@@ -223,58 +219,52 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
public SdmResponse<ChunkUploadMinioFileResp> chunkUploadToMinio(ChunkUploadMinioFileReq req) {
|
||||
ChunkUploadMinioFileResp resp = new ChunkUploadMinioFileResp();
|
||||
// 基础路径配置
|
||||
Long tenantId = ThreadLocalContext.getTenantId();
|
||||
// Long tenantId = 123456l;
|
||||
// Long userId = 9999l;
|
||||
Long userId= ThreadLocalContext.getUserId();
|
||||
// -2. 参数校验
|
||||
try {
|
||||
validateReq(req,tenantId,userId);
|
||||
validateReq(req);
|
||||
} catch (Exception e) {
|
||||
CoreLogger.error("validateReq error:{}", e.getMessage());
|
||||
return buildFailedResponse(resp,e.getMessage(),"",chunkBucket);
|
||||
return buildFailedResponse(resp,e.getMessage(),req);
|
||||
}
|
||||
// -1.确定文件夹
|
||||
String timestamp = String.valueOf(System.currentTimeMillis());
|
||||
// 合并目录
|
||||
String filePath = org.apache.commons.lang3.StringUtils.isNotBlank(req.getFileDirPath())?
|
||||
req.getFileDirPath():chunkBasePath + "/" + tenantId + "/" + userId +"/" + timestamp + "/";
|
||||
String filePath = getMinioFolderPath(req.getObjectKey());
|
||||
// 0. 一个文件直接传
|
||||
if(Objects.equals(req.getChunkTotal(),NumberConstants.ONE)&&
|
||||
Objects.equals(req.getChunk(),NumberConstants.ONE)){
|
||||
String finalFileName = filePath + req.getSourceFileName();
|
||||
Boolean b = minioService.chunkUpload(chunkBucket, req.getFile(), finalFileName);
|
||||
if(!b){
|
||||
return buildFailedResponse(resp,"单一文件上传失败",filePath,chunkBucket);
|
||||
return buildFailedResponse(resp,"单一文件上传失败",req);
|
||||
}
|
||||
return buildSuccessResponse(resp,finalFileName,filePath,chunkBucket);
|
||||
return buildSuccessResponse(resp,req,"");
|
||||
}
|
||||
|
||||
String tempDirPath = filePath +"temp/";
|
||||
// 碎片目录
|
||||
String tempDirPath = org.apache.commons.lang3.StringUtils.isBlank(req.getFileTempPath())?
|
||||
filePath +"temp/"+timestamp+"/":req.getFileTempPath();
|
||||
// 1. 保存当前分片到临时目录 1 2 3 4 ....temp
|
||||
String chunkFileName =tempDirPath+req.getChunk()+ PermConstants.CHUNK_TEMPFILE_SUFFIX;
|
||||
// 片文件上传到minio
|
||||
Boolean b = minioService.chunkUpload(chunkBucket, req.getFile(), chunkFileName);
|
||||
if(!b){
|
||||
return buildFailedResponse(resp,"chunkUpload第"+req.getChunk()+"次失败",filePath,chunkBucket);
|
||||
deleteTempFileAfterFailed(tempDirPath,chunkBucket);
|
||||
return buildFailedResponse(resp,"chunkUpload第"+req.getChunk()+"次失败",req);
|
||||
}
|
||||
// 2. 判断分片是否已全部上传完毕
|
||||
if (req.getChunk() < req.getChunkTotal()) {
|
||||
return buildSuccessResponse(resp,"",filePath,chunkBucket);
|
||||
return buildSuccessResponse(resp,req,tempDirPath);
|
||||
}
|
||||
// 3. 全部分片已经上传 => 自动合并
|
||||
String finalFileName = filePath + req.getSourceFileName();
|
||||
Boolean merge = minioService.merge(chunkBucket, tempDirPath, chunkBucket, finalFileName);
|
||||
if(!merge){
|
||||
return buildFailedResponse(resp,req.getSourceFileName()+"合并分片失败",filePath,chunkBucket);
|
||||
deleteTempFileAfterFailed(tempDirPath,chunkBucket);
|
||||
return buildFailedResponse(resp,req.getSourceFileName()+"合并分片失败",req);
|
||||
}
|
||||
// 4. 合并完成后删除临时目录
|
||||
String finalTempDirPath=tempDirPath;
|
||||
String finalChunkBucket=chunkBucket;
|
||||
CompletableFuture.runAsync(() -> {
|
||||
minioService.deleteDirectoryRecursively2(finalTempDirPath, finalChunkBucket);
|
||||
}, nonSensitiveTaskPool);
|
||||
return buildSuccessResponse(resp,finalFileName,filePath,chunkBucket);
|
||||
deleteTempFileAfterFailed(tempDirPath,chunkBucket);
|
||||
return buildSuccessResponse(resp,req,tempDirPath);
|
||||
}
|
||||
|
||||
|
||||
@@ -2148,10 +2138,8 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
/**
|
||||
* 参数校验
|
||||
*/
|
||||
private void validateReq(ChunkUploadMinioFileReq req, Long tenantId, Long userId) {
|
||||
private void validateReq(ChunkUploadMinioFileReq req) {
|
||||
// 基础参数校验
|
||||
Assert.notNull(tenantId, "租户ID不能为空");
|
||||
Assert.notNull(userId, "用户ID不能为空");
|
||||
Assert.hasText(req.getSourceFileName(), "原始文件名称不能为空");
|
||||
Assert.notNull(req.getChunk(), "分片编号不能为空");
|
||||
Assert.notNull(req.getChunkTotal(), "分片总数不能为空");
|
||||
@@ -2161,28 +2149,60 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
Assert.isTrue(req.getChunk() >= 1 && req.getChunk() <= req.getChunkTotal(),
|
||||
"分片编号非法:当前" + req.getChunk() + ",总分片" + req.getChunkTotal());
|
||||
Assert.isTrue(req.getChunkTotal() >= 1, "分片总数必须大于等于1");
|
||||
Assert.hasText(req.getObjectKey(), "文件存储位置不能为空");
|
||||
Assert.hasText(req.getUploadTaskId(), "文件上传任务Id不能为空");
|
||||
Assert.notNull(req.getBusinessId(), "文件绑定业务Id不能为空");
|
||||
if(req.getChunk() > NumberConstants.ONE) {
|
||||
Assert.hasText(req.getFileDirPath(), "分片父级目录不允许为空");
|
||||
Assert.hasText(req.getFileTempPath(), "分片临时目录不允许为空");
|
||||
}
|
||||
}
|
||||
|
||||
// 包含业务数据的响应体
|
||||
private SdmResponse<ChunkUploadMinioFileResp> buildSuccessResponse(ChunkUploadMinioFileResp resp, String finalFileName, String fileDirPath,String chunkBucket) {
|
||||
resp.setBucketName(chunkBucket);
|
||||
resp.setFilePath(finalFileName);
|
||||
resp.setFileDirPath(fileDirPath);
|
||||
// 成功时,错误信息通常为空
|
||||
resp.setErrMsg("");
|
||||
return SdmResponse.success(resp);
|
||||
}
|
||||
// 包含业务数据的响应体
|
||||
private SdmResponse<ChunkUploadMinioFileResp> buildSuccessResponse(ChunkUploadMinioFileResp resp, ChunkUploadMinioFileReq req,String fileTempPath) {
|
||||
resp.setResult(true);
|
||||
resp.setBusinessId(req.getBusinessId());
|
||||
resp.setUploadTaskId(req.getUploadTaskId());
|
||||
resp.setFileTempPath(fileTempPath);
|
||||
// 成功时,错误信息通常为空
|
||||
resp.setErrMsg("");
|
||||
return SdmResponse.success(resp);
|
||||
}
|
||||
|
||||
// 构建一个失败的响应对象
|
||||
private SdmResponse<ChunkUploadMinioFileResp> buildFailedResponse(ChunkUploadMinioFileResp resp, String errMsg, String fileDirPath,String chunkBucket) {
|
||||
resp.setBucketName(chunkBucket);
|
||||
resp.setFileDirPath(fileDirPath);
|
||||
// 构建一个失败的响应对象
|
||||
private SdmResponse<ChunkUploadMinioFileResp> buildFailedResponse(ChunkUploadMinioFileResp resp, String errMsg,ChunkUploadMinioFileReq req) {
|
||||
resp.setResult(false);
|
||||
resp.setBusinessId(req.getBusinessId());
|
||||
resp.setUploadTaskId(req.getUploadTaskId());
|
||||
// 如果 errMsg 为 null,设置为空字符串 ""
|
||||
resp.setErrMsg(errMsg != null ? errMsg : "");
|
||||
return SdmResponse.failed(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件路径字符串中获取文件夹路径(仅适用于 '/' 作为分隔符的情况)
|
||||
* @param filePath 完整文件路径(如:knowledge/20251111-yy/仿真地图 (46)_V1.xls)
|
||||
* @return 文件夹路径(如:knowledge/20251111-yy/),若输入为空或无分隔符则返回原路径
|
||||
*/
|
||||
private static String getMinioFolderPath(String filePath) {
|
||||
// 1. 校验输入合法性
|
||||
if (filePath == null || filePath.trim().isEmpty()) {
|
||||
return filePath;
|
||||
}
|
||||
// 2. 查找最后一个 '/' 分隔符的位置
|
||||
int lastSeparatorIndex = filePath.lastIndexOf("/");
|
||||
// 3. 若未找到分隔符(说明是单纯文件名,无路径),返回原字符串
|
||||
if (lastSeparatorIndex == -1) {
|
||||
return filePath;
|
||||
}
|
||||
// 4. 截取从开头到最后一个分隔符(包含分隔符)的子字符串
|
||||
return filePath.substring(0, lastSeparatorIndex + 1);
|
||||
}
|
||||
|
||||
// 分片上传,异常的时候删除临时目录,假如后面断点续传,某些场景的删除要最后确定失败再删除
|
||||
private void deleteTempFileAfterFailed(String tempFilePath,String finalChunkBucket) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
minioService.deleteDirectoryRecursively2(tempFilePath, finalChunkBucket);
|
||||
}, nonSensitiveTaskPool);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user