From 84e7f60dd40792da8154fd8fda8c65075b855cb9 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Thu, 14 Aug 2025 22:31:15 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E8=B0=83=E6=95=B4=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=20(mvn=20compile)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/satoken/SaTokenConfiguration.java | 5 +- .../constant/MultipartUploadConstants.java | 10 +- .../controller/MultipartUploadController.java | 23 +++- .../admin/system/dao/MultipartUploadDao.java | 18 ++- .../impl/RedisMultipartUploadDaoDaoImpl.java | 25 ++-- .../admin/system/factory/S3ClientFactory.java | 12 +- .../system/factory/StorageHandlerFactory.java | 20 +++- .../admin/system/handler/StorageHandler.java | 28 ++++- .../handler/impl/LocalStorageHandler.java | 28 +++-- .../system/handler/impl/S3StorageHandler.java | 108 +++++++++++------- .../model/req/MultipartUploadInitReq.java | 2 +- .../resp/file/MultipartUploadInitResp.java | 1 - .../service/MultipartUploadService.java | 18 ++- .../impl/MultipartUploadServiceImpl.java | 38 ++++-- 14 files changed, 241 insertions(+), 95 deletions(-) diff --git a/continew-server/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java b/continew-server/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java index ee9eb8cf..d400a25c 100644 --- a/continew-server/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java +++ b/continew-server/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java @@ -131,9 +131,10 @@ public class SaTokenConfiguration { }).filter(Objects::nonNull).toList(); if (!additionalExcludes.isEmpty()) { // 合并现有的 excludes 和新扫描到的 - String[] existingExcludes = Optional.ofNullable(properties.getSecurity().getExcludes()).orElse(new String[0]); + String[] existingExcludes = Optional.ofNullable(properties.getSecurity().getExcludes()) + .orElse(new String[0]); String[] combinedExcludes = Stream.concat(Arrays.stream(existingExcludes), additionalExcludes.stream()) - .toArray(String[]::new); + .toArray(String[]::new); properties.getSecurity().setExcludes(combinedExcludes); } log.debug("缓存 CRUD API 权限前缀完成:{}", CrudApiPermissionPrefixCache.getAll().values()); diff --git a/continew-system/src/main/java/top/continew/admin/system/constant/MultipartUploadConstants.java b/continew-system/src/main/java/top/continew/admin/system/constant/MultipartUploadConstants.java index ffce2425..c6ebbbd6 100644 --- a/continew-system/src/main/java/top/continew/admin/system/constant/MultipartUploadConstants.java +++ b/continew-system/src/main/java/top/continew/admin/system/constant/MultipartUploadConstants.java @@ -1,12 +1,12 @@ /* * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *

- * http://www.gnu.org/licenses/lgpl.html - *

+ * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/continew-system/src/main/java/top/continew/admin/system/controller/MultipartUploadController.java b/continew-system/src/main/java/top/continew/admin/system/controller/MultipartUploadController.java index c0a6399a..6e43d671 100644 --- a/continew-system/src/main/java/top/continew/admin/system/controller/MultipartUploadController.java +++ b/continew-system/src/main/java/top/continew/admin/system/controller/MultipartUploadController.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package top.continew.admin.system.controller; import io.swagger.v3.oas.annotations.Operation; @@ -47,8 +63,10 @@ public class MultipartUploadController { */ @Operation(summary = "上传分片", description = "上传单个分片") @PostMapping("/part") - public MultipartUploadResp uploadPart(@RequestPart("file") MultipartFile file, @RequestParam("uploadId") String uploadId, - @RequestParam("partNumber") Integer partNumber, @RequestParam("path") String path) { + public MultipartUploadResp uploadPart(@RequestPart("file") MultipartFile file, + @RequestParam("uploadId") String uploadId, + @RequestParam("partNumber") Integer partNumber, + @RequestParam("path") String path) { return multipartUploadService.uploadPart(file, uploadId, partNumber, path); } @@ -74,5 +92,4 @@ public class MultipartUploadController { multipartUploadService.cancelMultipartUpload(uploadId); } - } diff --git a/continew-system/src/main/java/top/continew/admin/system/dao/MultipartUploadDao.java b/continew-system/src/main/java/top/continew/admin/system/dao/MultipartUploadDao.java index 009d5ea3..9fe20289 100644 --- a/continew-system/src/main/java/top/continew/admin/system/dao/MultipartUploadDao.java +++ b/continew-system/src/main/java/top/continew/admin/system/dao/MultipartUploadDao.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package top.continew.admin.system.dao; import top.continew.admin.system.model.resp.file.FilePartInfo; @@ -72,7 +88,7 @@ public interface MultipartUploadDao { /** * 设置缓存分片信息 * - * @param uploadId 上传ID + * @param uploadId 上传ID * @param filePartInfo 分片信息 */ void setFilePart(String uploadId, FilePartInfo filePartInfo); diff --git a/continew-system/src/main/java/top/continew/admin/system/dao/impl/RedisMultipartUploadDaoDaoImpl.java b/continew-system/src/main/java/top/continew/admin/system/dao/impl/RedisMultipartUploadDaoDaoImpl.java index 022d82e2..fc29e5a8 100644 --- a/continew-system/src/main/java/top/continew/admin/system/dao/impl/RedisMultipartUploadDaoDaoImpl.java +++ b/continew-system/src/main/java/top/continew/admin/system/dao/impl/RedisMultipartUploadDaoDaoImpl.java @@ -1,12 +1,12 @@ /* * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *

- * http://www.gnu.org/licenses/lgpl.html - *

+ * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -95,7 +95,8 @@ public class RedisMultipartUploadDaoDaoImpl implements MultipartUploadDao { try { // 缓存初始化信息 - RedisUtils.set(key, JSONUtil.toJsonStr(initResp), Duration.ofHours(MultipartUploadConstants.DEFAULT_EXPIRE_HOURS)); + RedisUtils.set(key, JSONUtil.toJsonStr(initResp), Duration + .ofHours(MultipartUploadConstants.DEFAULT_EXPIRE_HOURS)); // 缓存元数据 if (metadata != null && !metadata.isEmpty()) { @@ -156,7 +157,7 @@ public class RedisMultipartUploadDaoDaoImpl implements MultipartUploadDao { public void deleteMultipartUploadAll(String uploadId) { this.deleteMultipartUpload(uploadId); this.deleteFileParts(uploadId); -// this.deleteMd5Mapping(); + // this.deleteMd5Mapping(); } @Override @@ -185,10 +186,10 @@ public class RedisMultipartUploadDaoDaoImpl implements MultipartUploadDao { } return entries.values() - .stream() - .map(value -> JSONUtil.toBean(value.toString(), FilePartInfo.class)) - .sorted(Comparator.comparing(FilePartInfo::getPartNumber)) - .collect(Collectors.toList()); + .stream() + .map(value -> JSONUtil.toBean(value.toString(), FilePartInfo.class)) + .sorted(Comparator.comparing(FilePartInfo::getPartNumber)) + .collect(Collectors.toList()); } catch (Exception e) { log.error("获取分片列表失败: uploadId={}", uploadId, e); return new ArrayList<>(); @@ -233,7 +234,7 @@ public class RedisMultipartUploadDaoDaoImpl implements MultipartUploadDao { if (value != null) { try { LocalDateTime expireTime = LocalDateTime.parse(value - .toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + .toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); if (now.isAfter(expireTime)) { expiredUploadIds.add(uploadId); } diff --git a/continew-system/src/main/java/top/continew/admin/system/factory/S3ClientFactory.java b/continew-system/src/main/java/top/continew/admin/system/factory/S3ClientFactory.java index 1cedb175..bb73cb0b 100644 --- a/continew-system/src/main/java/top/continew/admin/system/factory/S3ClientFactory.java +++ b/continew-system/src/main/java/top/continew/admin/system/factory/S3ClientFactory.java @@ -44,13 +44,13 @@ public class S3ClientFactory { String key = storage.getEndpoint() + "|" + storage.getAccessKey(); return CLIENT_CACHE.computeIfAbsent(key, k -> { StaticCredentialsProvider auth = StaticCredentialsProvider.create(AwsBasicCredentials.create(storage - .getAccessKey(), storage.getSecretKey())); + .getAccessKey(), storage.getSecretKey())); return S3Client.builder() - .credentialsProvider(auth) - .endpointOverride(URI.create(storage.getEndpoint())) - .region(Region.US_EAST_1) - .serviceConfiguration(S3Configuration.builder().chunkedEncodingEnabled(false).build()) - .build(); + .credentialsProvider(auth) + .endpointOverride(URI.create(storage.getEndpoint())) + .region(Region.US_EAST_1) + .serviceConfiguration(S3Configuration.builder().chunkedEncodingEnabled(false).build()) + .build(); }); } diff --git a/continew-system/src/main/java/top/continew/admin/system/factory/StorageHandlerFactory.java b/continew-system/src/main/java/top/continew/admin/system/factory/StorageHandlerFactory.java index 85eac0e6..f6b0c740 100644 --- a/continew-system/src/main/java/top/continew/admin/system/factory/StorageHandlerFactory.java +++ b/continew-system/src/main/java/top/continew/admin/system/factory/StorageHandlerFactory.java @@ -1,4 +1,4 @@ -package top.continew.admin.system.factory;/* +/* * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +14,21 @@ package top.continew.admin.system.factory;/* * limitations under the License. */ +package top.continew.admin.system.factory;/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import cn.hutool.core.util.StrUtil; import org.springframework.beans.factory.annotation.Autowired; @@ -30,6 +45,7 @@ import java.util.concurrent.ConcurrentHashMap; /** * 存储处理器工厂 *

按类型分发 StorageHandler

+ * * @author KAI * @since 2025/07/24 13:35 */ @@ -52,6 +68,6 @@ public class StorageHandlerFactory { */ public StorageHandler createHandler(StorageTypeEnum type) { return Optional.ofNullable(HANDLER_MAP.get(type)) - .orElseThrow(() -> new BaseException(StrUtil.format("不存在此类型存储处理器:{}: ", type))); + .orElseThrow(() -> new BaseException(StrUtil.format("不存在此类型存储处理器:{}: ", type))); } } \ No newline at end of file diff --git a/continew-system/src/main/java/top/continew/admin/system/handler/StorageHandler.java b/continew-system/src/main/java/top/continew/admin/system/handler/StorageHandler.java index 79adfc0d..4882de6e 100644 --- a/continew-system/src/main/java/top/continew/admin/system/handler/StorageHandler.java +++ b/continew-system/src/main/java/top/continew/admin/system/handler/StorageHandler.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package top.continew.admin.system.handler; import org.springframework.web.multipart.MultipartFile; @@ -30,7 +46,11 @@ public interface StorageHandler { * @param file 文件对象 * @return {@link MultipartUploadResp} 分片上传结果 */ - MultipartUploadResp uploadPart(StorageDO storageDO, String path, String uploadId, Integer partNumber, MultipartFile file); + MultipartUploadResp uploadPart(StorageDO storageDO, + String path, + String uploadId, + Integer partNumber, + MultipartFile file); /** * 合并分片 @@ -38,7 +58,11 @@ public interface StorageHandler { * @param storageDO 存储实体 * @param uploadId 上传Id */ - void completeMultipartUpload(StorageDO storageDO, List parts, String path, String uploadId, boolean needVerify); + void completeMultipartUpload(StorageDO storageDO, + List parts, + String path, + String uploadId, + boolean needVerify); /** * 清楚分片 diff --git a/continew-system/src/main/java/top/continew/admin/system/handler/impl/LocalStorageHandler.java b/continew-system/src/main/java/top/continew/admin/system/handler/impl/LocalStorageHandler.java index e8367a67..7fe44788 100644 --- a/continew-system/src/main/java/top/continew/admin/system/handler/impl/LocalStorageHandler.java +++ b/continew-system/src/main/java/top/continew/admin/system/handler/impl/LocalStorageHandler.java @@ -64,7 +64,9 @@ public class LocalStorageHandler implements StorageHandler { String parentPath = req.getParentPath(); String fileName = req.getFileName(); StrUtil.blankToDefault(parentPath, StrUtil.SLASH); - String relativePath = StrUtil.endWith(parentPath, StrUtil.SLASH) ? parentPath + fileName : parentPath + StrUtil.SLASH + fileName; + String relativePath = StrUtil.endWith(parentPath, StrUtil.SLASH) + ? parentPath + fileName + : parentPath + StrUtil.SLASH + fileName; try { // 创建临时目录用于存储分片 String tempDirPath = buildTempDirPath(bucket, uploadId); @@ -93,7 +95,11 @@ public class LocalStorageHandler implements StorageHandler { } @Override - public MultipartUploadResp uploadPart(StorageDO storageDO, String path, String uploadId, Integer partNumber, MultipartFile file) { + public MultipartUploadResp uploadPart(StorageDO storageDO, + String path, + String uploadId, + Integer partNumber, + MultipartFile file) { try { long size = file.getSize(); String bucket = storageDO.getBucketName(); @@ -135,7 +141,11 @@ public class LocalStorageHandler implements StorageHandler { } @Override - public void completeMultipartUpload(StorageDO storageDO, List parts, String path, String uploadId, boolean needVerify) { + public void completeMultipartUpload(StorageDO storageDO, + List parts, + String path, + String uploadId, + boolean needVerify) { String bucket = storageDO.getBucketName(); // 本地存储中,bucket是存储根路径 String tempDirPath = buildTempDirPath(bucket, uploadId); @@ -146,13 +156,13 @@ public class LocalStorageHandler implements StorageHandler { // 合并分片 try (OutputStream out = Files - .newOutputStream(targetPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + .newOutputStream(targetPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { // 按分片编号排序 List sortedParts = parts.stream() - .filter(MultipartUploadResp::isSuccess) - .sorted(Comparator.comparingInt(MultipartUploadResp::getPartNumber)) - .toList(); + .filter(MultipartUploadResp::isSuccess) + .sorted(Comparator.comparingInt(MultipartUploadResp::getPartNumber)) + .toList(); // 逐个读取并写入 for (MultipartUploadResp part : sortedParts) { @@ -206,10 +216,10 @@ public class LocalStorageHandler implements StorageHandler { * @return 临时目录路径 */ private String buildTempDirPath(String bucket, String uploadId) { - return StrUtil.appendIfMissing(bucket, File.separator) + MultipartUploadConstants.TEMP_DIR_NAME + File.separator + uploadId; + return StrUtil + .appendIfMissing(bucket, File.separator) + MultipartUploadConstants.TEMP_DIR_NAME + File.separator + uploadId; } - /** * 构建目标文件路径 * diff --git a/continew-system/src/main/java/top/continew/admin/system/handler/impl/S3StorageHandler.java b/continew-system/src/main/java/top/continew/admin/system/handler/impl/S3StorageHandler.java index 128f64ed..6183849f 100644 --- a/continew-system/src/main/java/top/continew/admin/system/handler/impl/S3StorageHandler.java +++ b/continew-system/src/main/java/top/continew/admin/system/handler/impl/S3StorageHandler.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package top.continew.admin.system.handler.impl; import cn.hutool.core.io.FileUtil; @@ -46,20 +62,22 @@ public class S3StorageHandler implements StorageHandler { String fileName = req.getFileName(); String contentType = req.getContentType(); StrUtil.blankToDefault(parentPath, StrUtil.SLASH); - String relativePath = StrUtil.endWith(parentPath, StrUtil.SLASH) ? parentPath + fileName : parentPath + StrUtil.SLASH + fileName; + String relativePath = StrUtil.endWith(parentPath, StrUtil.SLASH) + ? parentPath + fileName + : parentPath + StrUtil.SLASH + fileName; fileService.createParentDir(parentPath, storageDO); try { // 构建请求 CreateMultipartUploadRequest.Builder requestBuilder = CreateMultipartUploadRequest.builder() - .bucket(bucket) - .key(buildS3Key(relativePath)) - .contentType(contentType); + .bucket(bucket) + .key(buildS3Key(relativePath)) + .contentType(contentType); // 添加元数据 暂时注释掉 mataData传递中文会导致签名校验不通过 -// if (metaData != null && !metaData.isEmpty()) { -// requestBuilder.metadata(metaData); -// } + // if (metaData != null && !metaData.isEmpty()) { + // requestBuilder.metadata(metaData); + // } S3Client s3Client = s3ClientFactory.getClient(storageDO); log.info("S3初始化分片上传: bucket={}, key={}, contentType={}", bucket, buildS3Key(relativePath), contentType); @@ -85,14 +103,17 @@ public class S3StorageHandler implements StorageHandler { log.info("S3初始化分片上传成功: uploadId={}, path={}", uploadId, relativePath); return result; - } catch (Exception e) { throw new BaseException("S3初始化分片上传失败: " + e.getMessage(), e); } } @Override - public MultipartUploadResp uploadPart(StorageDO storageDO, String path, String uploadId, Integer partNumber, MultipartFile file) { + public MultipartUploadResp uploadPart(StorageDO storageDO, + String path, + String uploadId, + Integer partNumber, + MultipartFile file) { try { String bucket = storageDO.getBucketName(); // 读取数据到内存(注意:实际使用时可能需要优化大文件处理) @@ -100,12 +121,12 @@ public class S3StorageHandler implements StorageHandler { // 构建请求 UploadPartRequest request = UploadPartRequest.builder() - .bucket(bucket) - .key(buildS3Key(path)) - .uploadId(uploadId) - .partNumber(partNumber) - .contentLength((long) bytes.length) - .build(); + .bucket(bucket) + .key(buildS3Key(path)) + .uploadId(uploadId) + .partNumber(partNumber) + .contentLength((long)bytes.length) + .build(); // 执行上传 S3Client s3Client = s3ClientFactory.getClient(storageDO); @@ -125,13 +146,18 @@ public class S3StorageHandler implements StorageHandler { result.setPartNumber(partNumber); result.setSuccess(false); result.setErrorMessage(e.getMessage()); - log.error("S3上传分片失败: partNumber={} for key={} with uploadId={} errorMessage={}", partNumber, path, uploadId, e.getMessage()); + log.error("S3上传分片失败: partNumber={} for key={} with uploadId={} errorMessage={}", partNumber, path, uploadId, e + .getMessage()); return result; } } @Override - public void completeMultipartUpload(StorageDO storageDO, List parts, String path, String uploadId, boolean needVerify) { + public void completeMultipartUpload(StorageDO storageDO, + List parts, + String path, + String uploadId, + boolean needVerify) { if (path == null) { throw new BaseException("无效的uploadId: " + uploadId); } @@ -144,18 +170,18 @@ public class S3StorageHandler implements StorageHandler { } // 构建已完成的分片列表 List completedParts = parts.stream() - .filter(MultipartUploadResp::isSuccess) - .map(part -> CompletedPart.builder().partNumber(part.getPartNumber()).eTag(part.getPartETag()).build()) - .sorted(Comparator.comparingInt(CompletedPart::partNumber)) - .collect(Collectors.toList()); + .filter(MultipartUploadResp::isSuccess) + .map(part -> CompletedPart.builder().partNumber(part.getPartNumber()).eTag(part.getPartETag()).build()) + .sorted(Comparator.comparingInt(CompletedPart::partNumber)) + .collect(Collectors.toList()); // 构建请求 CompleteMultipartUploadRequest request = CompleteMultipartUploadRequest.builder() - .bucket(bucket) - .key(buildS3Key(path)) - .uploadId(uploadId) - .multipartUpload(CompletedMultipartUpload.builder().parts(completedParts).build()) - .build(); + .bucket(bucket) + .key(buildS3Key(path)) + .uploadId(uploadId) + .multipartUpload(CompletedMultipartUpload.builder().parts(completedParts).build()) + .build(); // 完成上传 s3Client.completeMultipartUpload(request); @@ -169,26 +195,25 @@ public class S3StorageHandler implements StorageHandler { S3Client s3Client = s3ClientFactory.getClient(storageDO); // 列出所有未完成的分片上传 - ListMultipartUploadsRequest listRequest = ListMultipartUploadsRequest.builder() - .bucket(bucket) - .build(); + ListMultipartUploadsRequest listRequest = ListMultipartUploadsRequest.builder().bucket(bucket).build(); ListMultipartUploadsResponse listResponse = s3Client.listMultipartUploads(listRequest); // 查找匹配的上传任务 - Optional targetUpload = listResponse.uploads().stream() - .filter(upload -> upload.uploadId().equals(uploadId)) - .findFirst(); + Optional targetUpload = listResponse.uploads() + .stream() + .filter(upload -> upload.uploadId().equals(uploadId)) + .findFirst(); if (targetUpload.isPresent()) { MultipartUpload upload = targetUpload.get(); // 取消分片上传 AbortMultipartUploadRequest abortRequest = AbortMultipartUploadRequest.builder() - .bucket(bucket) - .key(upload.key()) - .uploadId(uploadId) - .build(); + .bucket(bucket) + .key(upload.key()) + .uploadId(uploadId) + .build(); s3Client.abortMultipartUpload(abortRequest); log.info("S3清理分片上传成功: bucket={}, key={}, uploadId={}", bucket, upload.key(), uploadId); @@ -207,14 +232,17 @@ public class S3StorageHandler implements StorageHandler { return StorageTypeEnum.OSS; } - /** * 列出已上传的分片 */ public List listParts(String bucket, String path, String uploadId, S3Client s3Client) { try { // 构建请求 - ListPartsRequest request = ListPartsRequest.builder().bucket(bucket).key(buildS3Key(path)).uploadId(uploadId).build(); + ListPartsRequest request = ListPartsRequest.builder() + .bucket(bucket) + .key(buildS3Key(path)) + .uploadId(uploadId) + .build(); // 获取分片列表 ListPartsResponse response = s3Client.listParts(request); @@ -241,10 +269,10 @@ public class S3StorageHandler implements StorageHandler { */ private void validateParts(List recordParts, List s3Parts) { Map recordMap = recordParts.stream() - .collect(Collectors.toMap(MultipartUploadResp::getPartNumber, MultipartUploadResp::getPartETag)); + .collect(Collectors.toMap(MultipartUploadResp::getPartNumber, MultipartUploadResp::getPartETag)); Map s3Map = s3Parts.stream() - .collect(Collectors.toMap(MultipartUploadResp::getPartNumber, MultipartUploadResp::getPartETag)); + .collect(Collectors.toMap(MultipartUploadResp::getPartNumber, MultipartUploadResp::getPartETag)); // 检查分片数量 if (recordMap.size() != s3Map.size()) { diff --git a/continew-system/src/main/java/top/continew/admin/system/model/req/MultipartUploadInitReq.java b/continew-system/src/main/java/top/continew/admin/system/model/req/MultipartUploadInitReq.java index 883951ae..d79f39e6 100644 --- a/continew-system/src/main/java/top/continew/admin/system/model/req/MultipartUploadInitReq.java +++ b/continew-system/src/main/java/top/continew/admin/system/model/req/MultipartUploadInitReq.java @@ -77,5 +77,5 @@ public class MultipartUploadInitReq implements Serializable { * 文件元信息 */ @Schema(description = "文件元信息") - private Map metaData; + private Map metaData; } \ No newline at end of file diff --git a/continew-system/src/main/java/top/continew/admin/system/model/resp/file/MultipartUploadInitResp.java b/continew-system/src/main/java/top/continew/admin/system/model/resp/file/MultipartUploadInitResp.java index f948a845..74336956 100644 --- a/continew-system/src/main/java/top/continew/admin/system/model/resp/file/MultipartUploadInitResp.java +++ b/continew-system/src/main/java/top/continew/admin/system/model/resp/file/MultipartUploadInitResp.java @@ -116,5 +116,4 @@ public class MultipartUploadInitResp implements Serializable { @Schema(description = "已上传分片编号集合") private Set uploadedPartNumbers; - } \ No newline at end of file diff --git a/continew-system/src/main/java/top/continew/admin/system/service/MultipartUploadService.java b/continew-system/src/main/java/top/continew/admin/system/service/MultipartUploadService.java index e89a0d2b..02581a2b 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/MultipartUploadService.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/MultipartUploadService.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package top.continew.admin.system.service; import org.springframework.web.multipart.MultipartFile; @@ -16,7 +32,7 @@ public interface MultipartUploadService { MultipartUploadInitResp initMultipartUpload(MultipartUploadInitReq multiPartUploadInitReq); - MultipartUploadResp uploadPart(MultipartFile file, String uploadId, Integer partNumber,String path); + MultipartUploadResp uploadPart(MultipartFile file, String uploadId, Integer partNumber, String path); FileDO completeMultipartUpload(String uploadId); diff --git a/continew-system/src/main/java/top/continew/admin/system/service/impl/MultipartUploadServiceImpl.java b/continew-system/src/main/java/top/continew/admin/system/service/impl/MultipartUploadServiceImpl.java index d3bdee9e..b4e111b8 100644 --- a/continew-system/src/main/java/top/continew/admin/system/service/impl/MultipartUploadServiceImpl.java +++ b/continew-system/src/main/java/top/continew/admin/system/service/impl/MultipartUploadServiceImpl.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package top.continew.admin.system.service.impl; import cn.hutool.core.io.FileUtil; @@ -51,17 +67,18 @@ public class MultipartUploadServiceImpl implements MultipartUploadService { // 后续可以增加storageCode参数 指定某个存储平台 当前设计是默认存储平台 StorageDO storageDO = storageService.getByCode(null); // 根据文件Md5查询当前存储平台是否初始化过分片 - String uploadId = multipartUploadDao.getUploadIdByMd5(multiPartUploadInitReq - .getFileMd5()); + String uploadId = multipartUploadDao.getUploadIdByMd5(multiPartUploadInitReq.getFileMd5()); if (StrUtil.isNotBlank(uploadId)) { MultipartUploadInitResp multipartUpload = multipartUploadDao.getMultipartUpload(uploadId); //对比存储平台和分片大小是否一致 一致则返回结果 - if (multipartUpload != null - && multipartUpload.getPartSize().equals(MultipartUploadConstants.MULTIPART_UPLOAD_PART_SIZE) - && multipartUpload.getPlatform().equals(storageDO.getCode())) { + if (multipartUpload != null && multipartUpload.getPartSize() + .equals(MultipartUploadConstants.MULTIPART_UPLOAD_PART_SIZE) && multipartUpload.getPlatform() + .equals(storageDO.getCode())) { // 获取已上传分片信息 List fileParts = multipartUploadDao.getFileParts(uploadId); - Set partNumbers = fileParts.stream().map(FilePartInfo::getPartNumber).collect(Collectors.toSet()); + Set partNumbers = fileParts.stream() + .map(FilePartInfo::getPartNumber) + .collect(Collectors.toSet()); multipartUpload.setUploadedPartNumbers(partNumbers); return multipartUpload; } @@ -71,7 +88,8 @@ public class MultipartUploadServiceImpl implements MultipartUploadService { StorageHandler storageHandler = storageHandlerFactory.createHandler(storageDO.getType()); //文件元信息 Map metaData = multiPartUploadInitReq.getMetaData(); - MultipartUploadInitResp multipartUploadInitResp = storageHandler.initMultipartUpload(storageDO, multiPartUploadInitReq); + MultipartUploadInitResp multipartUploadInitResp = storageHandler + .initMultipartUpload(storageDO, multiPartUploadInitReq); // 缓存文件信息,md5和uploadId映射 multipartUploadDao.setMultipartUpload(multipartUploadInitResp.getUploadId(), multipartUploadInitResp, metaData); multipartUploadDao.setMd5Mapping(multiPartUploadInitReq.getFileMd5(), multipartUploadInitResp.getUploadId()); @@ -174,9 +192,9 @@ public class MultipartUploadServiceImpl implements MultipartUploadService { // 检查是否所有分片都成功 List failedParts = parts.stream() - .filter(part -> !part.isSuccess()) - .map(MultipartUploadResp::getPartNumber) - .toList(); + .filter(part -> !part.isSuccess()) + .map(MultipartUploadResp::getPartNumber) + .toList(); if (!failedParts.isEmpty()) { throw new BaseException("存在失败的分片: " + failedParts);