From 7ead337165f930771b86a1b5d36f4f4bdacfbb12 Mon Sep 17 00:00:00 2001 From: QAQ_Z <958142070@qq.com> Date: Thu, 11 Sep 2025 09:06:31 +0800 Subject: [PATCH] =?UTF-8?q?perf(storage):=20=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=B1=BB=E5=9E=8B=E6=9E=84=E5=BB=BA=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=92=8C=E9=BB=98=E8=AE=A4=E5=AD=98=E5=82=A8=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E5=BB=B6=E8=BF=9F=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化 FileWrapper 类,增加从请求中获取文件名和内容类型的逻辑 - 改进 UploadPretreatment 类,延迟获取默认存储平台 Co-authored-by: QAQ_Z<958142070@qq.com> # message auto-generated for no-merge-commit merge: merge storage into dev 优化文件类型构建逻辑和默认存储平台延迟加载 Created-by: QAQ_Z Commit-by: QAQ_Z Merged-by: Charles_7c Description: ## PR 类型 - [x] 新 feature - [x] Bug 修复 - [ ] 功能增强 - [ ] 文档变更 - [ ] 代码样式变更 - [ ] 重构 - [ ] 性能改进 - [ ] 单元测试 - [ ] CI/CD - [ ] 其他 ## PR 目的 优化文件类型构建逻辑和默认存储平台延迟加载 ## 解决方案 ## PR 测试 ## Changelog | 模块 | Changelog | Related issues | |-----|-----------| -------------- | | continew-starter-storage | 优化文件类型构建逻辑和默认存储平台延迟加载 | | ## 其他信息 ## 提交前确认 - [x] PR 代码经过了完整测试,并且通过了代码规范检查 - [ ] 已经完整填写 Changelog,并链接到了相关 issues - [x] PR 代码将要提交到 dev 分支 See merge request: continew/continew-starter!5 --- .../storage/core/UploadPretreatment.java | 10 +- .../storage/domain/file/FileWrapper.java | 123 +++++++++++++++--- .../storage/engine/StorageStrategyRouter.java | 4 +- 3 files changed, 114 insertions(+), 23 deletions(-) diff --git a/continew-starter-storage/src/main/java/top/continew/starter/storage/core/UploadPretreatment.java b/continew-starter-storage/src/main/java/top/continew/starter/storage/core/UploadPretreatment.java index fa2f8cda..be787c72 100644 --- a/continew-starter-storage/src/main/java/top/continew/starter/storage/core/UploadPretreatment.java +++ b/continew-starter-storage/src/main/java/top/continew/starter/storage/core/UploadPretreatment.java @@ -16,11 +16,11 @@ package top.continew.starter.storage.core; +import cn.hutool.core.util.StrUtil; import org.springframework.web.multipart.MultipartFile; import top.continew.starter.storage.domain.model.context.UploadContext; import top.continew.starter.storage.domain.model.req.ThumbnailSize; import top.continew.starter.storage.domain.model.resp.FileInfo; -import top.continew.starter.storage.processor.preprocess.*; import top.continew.starter.storage.processor.progress.UploadProgressListener; import top.continew.starter.storage.service.FileProcessor; @@ -45,7 +45,6 @@ public class UploadPretreatment { this.storageService = storageService; this.context = new UploadContext(); this.context.setFile(file); - this.context.setPlatform(storageService.getDefaultPlatform()); } /** @@ -146,6 +145,8 @@ public class UploadPretreatment { * @return {@link FileInfo } */ public FileInfo upload() { + + // 添加文件处理器 for (FileProcessor processor : processors) { storageService.addProcessor(processor); } @@ -155,6 +156,11 @@ public class UploadPretreatment { storageService.onProgress(progressListener); } + // 如果没有设置平台,则获取默认平台 (延迟获取默认存储平台) + if (StrUtil.isBlank(context.getPlatform())) { + context.setPlatform(storageService.getDefaultPlatform()); + } + // 执行上传 return storageService.upload(context); } diff --git a/continew-starter-storage/src/main/java/top/continew/starter/storage/domain/file/FileWrapper.java b/continew-starter-storage/src/main/java/top/continew/starter/storage/domain/file/FileWrapper.java index 8d9b35b2..a00a72cc 100644 --- a/continew-starter-storage/src/main/java/top/continew/starter/storage/domain/file/FileWrapper.java +++ b/continew-starter-storage/src/main/java/top/continew/starter/storage/domain/file/FileWrapper.java @@ -17,13 +17,21 @@ package top.continew.starter.storage.domain.file; import cn.hutool.json.JSONUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.multipart.MultipartFile; import top.continew.starter.storage.common.exception.StorageException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.Collection; /** * 文件包装器,用于统一处理不同类型的输入 @@ -33,6 +41,8 @@ import java.nio.charset.StandardCharsets; */ public class FileWrapper { + private static final Logger log = LoggerFactory.getLogger(FileWrapper.class); + private MultipartFile multipartFile; private byte[] bytes; private InputStream inputStream; @@ -57,27 +67,51 @@ public class FileWrapper { /** * 从 byte[] 创建 + * + * @param bytes 字节 + * @param filename 文件名 + * @param contentType 内容类型 + * @return {@link FileWrapper } */ public static FileWrapper of(byte[] bytes, String filename, String contentType) { - if (filename == null || filename.trim().isEmpty()) { - throw new StorageException("文件名不能为空"); - } - if (contentType == null || contentType.trim().isEmpty()) { - throw new StorageException("文件类型不能为空"); - } - - FileWrapper wrapper = new FileWrapper(); + FileWrapper wrapper = createBaseWrapper(filename, contentType); wrapper.bytes = bytes; - wrapper.originalFilename = filename; - wrapper.contentType = contentType; - wrapper.size = bytes.length; + wrapper.size = bytes != null ? bytes.length : 0; return wrapper; } /** * 从 InputStream 创建 + * + * @param inputStream 输入流 + * @param filename 文件名 + * @param contentType 内容类型 + * @return {@link FileWrapper } */ public static FileWrapper of(InputStream inputStream, String filename, String contentType) { + FileWrapper wrapper = createBaseWrapper(filename, contentType); + wrapper.inputStream = inputStream; + wrapper.size = -1; + return wrapper; + } + + /** + * 创建基本包装器 + * + * @param filename 文件名 + * @param contentType 内容类型 + * @return {@link FileWrapper } + */ + private static FileWrapper createBaseWrapper(String filename, String contentType) { + // 如果没有提供,尝试从请求中获取 + if (filename == null) { + filename = tryGetFilenameFromRequest(); + } + if (contentType == null) { + contentType = tryGetContentTypeFromRequest(); + } + + // 最终校验 if (filename == null || filename.trim().isEmpty()) { throw new StorageException("文件名不能为空"); } @@ -86,13 +120,70 @@ public class FileWrapper { } FileWrapper wrapper = new FileWrapper(); - wrapper.inputStream = inputStream; wrapper.originalFilename = filename; wrapper.contentType = contentType; - wrapper.size = -1; return wrapper; } + /** + * 尝试从当前 HTTP 请求中获取文件名 + */ + private static String tryGetFilenameFromRequest() { + try { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes instanceof ServletRequestAttributes) { + HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest(); + + // 检查是否是 multipart 请求 + String requestContentType = request.getContentType(); + if (requestContentType != null && requestContentType.toLowerCase().startsWith("multipart/")) { + Collection parts = request.getParts(); + + for (Part part : parts) { + String submittedFilename = part.getSubmittedFileName(); + if (submittedFilename != null && !submittedFilename.trim().isEmpty()) { + return submittedFilename; + } + } + } + } + } catch (Exception e) { + log.debug("从请求中获取文件名时发生异常: {}", e.getMessage()); + } + return null; + } + + /** + * 尝试从当前 HTTP 请求中获取 ContentType + */ + private static String tryGetContentTypeFromRequest() { + try { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes instanceof ServletRequestAttributes) { + HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest(); + + // 检查是否是 multipart 请求 + String requestContentType = request.getContentType(); + if (requestContentType != null && requestContentType.toLowerCase().startsWith("multipart/")) { + Collection parts = request.getParts(); + + for (Part part : parts) { + // 只处理文件部分 + if (part.getSubmittedFileName() != null) { + String partContentType = part.getContentType(); + if (partContentType != null && !partContentType.trim().isEmpty()) { + return partContentType; + } + } + } + } + } + } catch (Exception e) { + log.debug("从请求中获取 ContentType 时发生异常: {}", e.getMessage()); + } + return null; + } + /** * 从 Object 创建(智能识别) */ @@ -115,17 +206,11 @@ public class FileWrapper { // 如果是 byte[] if (obj instanceof byte[]) { - if (filename == null || contentType == null) { - throw new StorageException("byte[] 类型必须指定文件名和文件类型"); - } return of((byte[])obj, filename, contentType); } // 如果是 InputStream if (obj instanceof InputStream) { - if (filename == null || contentType == null) { - throw new StorageException("InputStream 类型必须指定文件名和文件类型"); - } return of((InputStream)obj, filename, contentType); } diff --git a/continew-starter-storage/src/main/java/top/continew/starter/storage/engine/StorageStrategyRouter.java b/continew-starter-storage/src/main/java/top/continew/starter/storage/engine/StorageStrategyRouter.java index 6dcfb86a..21b7038d 100644 --- a/continew-starter-storage/src/main/java/top/continew/starter/storage/engine/StorageStrategyRouter.java +++ b/continew-starter-storage/src/main/java/top/continew/starter/storage/engine/StorageStrategyRouter.java @@ -93,8 +93,8 @@ public class StorageStrategyRouter implements ApplicationListener Optional.ofNullable(configStrategies.get(platform))) - .orElseThrow(() -> new StorageException(String.format("不支持的存储平台: %s", platform))); + .or(() -> Optional.ofNullable(configStrategies.get(platform))) + .orElseThrow(() -> new StorageException(String.format("不支持的存储平台: %s", platform))); } /**