mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-10-15 10:57:09 +08:00
perf(storage): 优化文件类型构建逻辑和默认存储平台延迟加载
- 优化 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!在提交之前,请务必确保您 PR 的代码经过了完整测试,并且通过了代码规范检查。 --> <!-- 在 [] 中输入 x 来勾选) --> ## PR 类型 <!-- 您的 PR 引入了哪种类型的变更? --> <!-- 只支持选择一种类型,如果有多种类型,可以在更新日志中增加 “类型” 列。 --> - [x] 新 feature - [x] Bug 修复 - [ ] 功能增强 - [ ] 文档变更 - [ ] 代码样式变更 - [ ] 重构 - [ ] 性能改进 - [ ] 单元测试 - [ ] CI/CD - [ ] 其他 ## PR 目的 <!-- 描述一下您的 PR 解决了什么问题。如果可以,请链接到相关 issues。 --> 优化文件类型构建逻辑和默认存储平台延迟加载 ## 解决方案 <!-- 详细描述您是如何解决的问题 --> ## PR 测试 <!-- 如果可以,请为您的 PR 添加或更新单元测试。 --> <!-- 请描述一下您是如何测试 PR 的。例如:创建/更新单元测试或添加相关的截图。 --> ## Changelog | 模块 | Changelog | Related issues | |-----|-----------| -------------- | | continew-starter-storage | 优化文件类型构建逻辑和默认存储平台延迟加载 | | <!-- 如果有多种类型的变更,可以在变更日志表中增加 “类型” 列,该列的值与上方 “PR 类型” 相同。 --> <!-- Related issues 格式为 Closes #<issue号>,或者 Fixes #<issue号>,或者 Resolves #<issue号>。 --> ## 其他信息 <!-- 请描述一下还有哪些注意事项。例如:如果引入了一个不向下兼容的变更,请描述其影响。 --> ## 提交前确认 - [x] PR 代码经过了完整测试,并且通过了代码规范检查 - [ ] 已经完整填写 Changelog,并链接到了相关 issues - [x] PR 代码将要提交到 dev 分支 See merge request: continew/continew-starter!5
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
@@ -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<Part> 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<Part> 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);
|
||||
}
|
||||
|
||||
|
@@ -93,8 +93,8 @@ public class StorageStrategyRouter implements ApplicationListener<ApplicationEve
|
||||
*/
|
||||
private StorageStrategy getOriginalStrategy(String platform) {
|
||||
return Optional.ofNullable(dynamicStrategies.get(platform))
|
||||
.or(() -> 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)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user