From 77d634038c796215aeb531790068012bdf73b900 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 28 Feb 2024 18:59:33 +0800 Subject: [PATCH] ✨ 全局:简化 file 组件,融合到 infra 模块 --- yudao-dependencies/pom.xml | 5 ----- yudao-framework/pom.xml | 1 - yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java | 15 +++++++++++++++ yudao-framework/yudao-spring-boot-starter-file/pom.xml | 83 ----------------------------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/config/YudaoFileAutoConfiguration.java | 21 --------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/AbstractFileClient.java | 69 --------------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java | 55 ------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientConfig.java | 16 ---------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactory.java | 22 ---------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactoryImpl.java | 56 -------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClient.java | 48 ------------------------------------------------ yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClientConfig.java | 24 ------------------------ yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileContentFrameworkDAO.java | 36 ------------------------------------ yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClient.java | 77 ----------------------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientConfig.java | 59 ----------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClient.java | 52 ---------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientConfig.java | 30 ------------------------------ yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/FilePresignedUrlRespDTO.java | 29 ----------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java | 134 -------------------------------------------------------------------------------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java | 77 ----------------------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClient.java | 61 ------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientConfig.java | 52 ---------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/enums/FileStorageEnum.java | 55 ------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/utils/FileTypeUtils.java | 48 ------------------------------------------------ yudao-framework/yudao-spring-boot-starter-file/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports | 1 - yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/config/package-info.java | 4 ---- yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientTest.java | 39 --------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientTest.java | 27 --------------------------- yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientTest.java | 117 --------------------------------------------------------------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientTest.java | 37 ------------------------------------- yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/enums/package-info.java | 4 ---- yudao-framework/yudao-spring-boot-starter-file/src/test/resources/file/erweima.jpg | Bin 18385 -> 0 bytes yudao-module-infra/yudao-module-infra-biz/pom.xml | 17 +++++++++++++++-- yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java | 2 +- yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java | 3 ++- yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentDAOImpl.java | 46 ---------------------------------------------- yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java | 16 ++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java | 21 +++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java | 16 ++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java | 24 ++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java | 24 ++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java | 30 ++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java | 29 +++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java | 12 ++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java | 2 +- yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java | 8 ++++---- yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java | 6 +++--- yudao-module-infra/yudao-module-infra-biz/src/main/resources/file/erweima.jpg | Bin 18385 -> 0 bytes yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java | 41 +++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java | 29 +++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java | 39 +++++++++++++++++++++++++++++++++++++++ yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java | 12 ++++++------ yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java | 2 +- 68 files changed, 1341 insertions(+), 1408 deletions(-) delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/pom.xml delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/config/YudaoFileAutoConfiguration.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/AbstractFileClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientConfig.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactory.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactoryImpl.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClientConfig.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileContentFrameworkDAO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientConfig.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientConfig.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/FilePresignedUrlRespDTO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientConfig.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/enums/FileStorageEnum.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/utils/FileTypeUtils.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/config/package-info.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientTest.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientTest.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientTest.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientTest.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/enums/package-info.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-file/src/test/resources/file/erweima.jpg delete mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentDAOImpl.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 0344bfd..f073e67 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -567,11 +567,6 @@ ${okhttp3.version} - cn.iocoder.boot - yudao-spring-boot-starter-file - ${revision} - - io.minio minio ${minio.version} diff --git a/yudao-framework/pom.xml b/yudao-framework/pom.xml index 9595bbd..446fd4d 100644 --- a/yudao-framework/pom.xml +++ b/yudao-framework/pom.xml @@ -16,7 +16,6 @@ yudao-spring-boot-starter-web yudao-spring-boot-starter-security - yudao-spring-boot-starter-file yudao-spring-boot-starter-monitor yudao-spring-boot-starter-protection yudao-spring-boot-starter-job diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java index 2c7288e..2cbe93c 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java @@ -136,6 +136,21 @@ public class JsonUtils { } } + /** + * 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null + * + * @param text 字符串 + * @param typeReference 类型引用 + * @return 指定类型的对象 + */ + public static T parseObjectQuietly(String text, TypeReference typeReference) { + try { + return objectMapper.readValue(text, typeReference); + } catch (IOException e) { + return null; + } + } + public static List parseArray(String text, Class clazz) { if (StrUtil.isEmpty(text)) { return new ArrayList<>(); diff --git a/yudao-framework/yudao-spring-boot-starter-file/pom.xml b/yudao-framework/yudao-spring-boot-starter-file/pom.xml deleted file mode 100644 index 5ce48a8..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/pom.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - yudao-framework - cn.iocoder.boot - ${revision} - - 4.0.0 - yudao-spring-boot-starter-file - - ${project.artifactId} - 文件客户端,支持多种存储器 - 1. file:本地磁盘 - 2. ftp:FTP 服务器 - 2. sftp:SFTP 服务器 - 4. db:数据库 - 5. s3:支持 S3 协议的云存储服务,例如说 MinIO、阿里云、华为云、腾讯云、七牛云等等 - - https://github.com/YunaiV/ruoyi-vue-pro - - - - cn.iocoder.boot - yudao-common - - - - - org.springframework.boot - spring-boot-starter - - - - - org.springframework.boot - spring-boot-starter-validation - - - - org.slf4j - slf4j-api - - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.core - jackson-core - - - - commons-net - commons-net - - - com.jcraft - jsch - - - - org.apache.tika - tika-core - - - - - io.minio - minio - - - - - cn.iocoder.boot - yudao-spring-boot-starter-test - test - - - - diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/config/YudaoFileAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/config/YudaoFileAutoConfiguration.java deleted file mode 100644 index e22ee3d..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/config/YudaoFileAutoConfiguration.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.framework.file.config; - -import cn.iocoder.yudao.framework.file.core.client.FileClientFactory; -import cn.iocoder.yudao.framework.file.core.client.FileClientFactoryImpl; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; - -/** - * 文件配置类 - * - * @author 芋道源码 - */ -@AutoConfiguration -public class YudaoFileAutoConfiguration { - - @Bean - public FileClientFactory fileClientFactory() { - return new FileClientFactoryImpl(); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/AbstractFileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/AbstractFileClient.java deleted file mode 100644 index b21ccc0..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/AbstractFileClient.java +++ /dev/null @@ -1,69 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client; - -import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; - -/** - * 文件客户端的抽象类,提供模板方法,减少子类的冗余代码 - * - * @author 芋道源码 - */ -@Slf4j -public abstract class AbstractFileClient implements FileClient { - - /** - * 配置编号 - */ - private final Long id; - /** - * 文件配置 - */ - protected Config config; - - public AbstractFileClient(Long id, Config config) { - this.id = id; - this.config = config; - } - - /** - * 初始化 - */ - public final void init() { - doInit(); - log.debug("[init][配置({}) 初始化完成]", config); - } - - /** - * 自定义初始化 - */ - protected abstract void doInit(); - - public final void refresh(Config config) { - // 判断是否更新 - if (config.equals(this.config)) { - return; - } - log.info("[refresh][配置({})发生变化,重新初始化]", config); - this.config = config; - // 初始化 - this.init(); - } - - @Override - public Long getId() { - return id; - } - - /** - * 格式化文件的 URL 访问地址 - * 使用场景:local、ftp、db,通过 FileController 的 getFile 来获取文件内容 - * - * @param domain 自定义域名 - * @param path 文件路径 - * @return URL 访问地址 - */ - protected String formatFileUrl(String domain, String path) { - return StrUtil.format("{}/admin-api/infra/file/{}/get/{}", domain, getId(), path); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java deleted file mode 100644 index 2944ca7..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client; - -import cn.iocoder.yudao.framework.file.core.client.s3.FilePresignedUrlRespDTO; - -/** - * 文件客户端 - * - * @author 芋道源码 - */ -public interface FileClient { - - /** - * 获得客户端编号 - * - * @return 客户端编号 - */ - Long getId(); - - /** - * 上传文件 - * - * @param content 文件流 - * @param path 相对路径 - * @return 完整路径,即 HTTP 访问地址 - * @throws Exception 上传文件时,抛出 Exception 异常 - */ - String upload(byte[] content, String path, String type) throws Exception; - - /** - * 删除文件 - * - * @param path 相对路径 - * @throws Exception 删除文件时,抛出 Exception 异常 - */ - void delete(String path) throws Exception; - - /** - * 获得文件的内容 - * - * @param path 相对路径 - * @return 文件的内容 - */ - byte[] getContent(String path) throws Exception; - - /** - * 获得文件预签名地址 - * - * @param path 相对路径 - * @return 文件预签名地址 - */ - default FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception { - throw new UnsupportedOperationException("不支持的操作"); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientConfig.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientConfig.java deleted file mode 100644 index 9461c05..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientConfig.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client; - -import com.fasterxml.jackson.annotation.JsonTypeInfo; - -/** - * 文件客户端的配置 - * 不同实现的客户端,需要不同的配置,通过子类来定义 - * - * @author 芋道源码 - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) -// @JsonTypeInfo 注解的作用,Jackson 多态 -// 1. 序列化到时数据库时,增加 @class 属性。 -// 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 -public interface FileClientConfig { -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactory.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactory.java deleted file mode 100644 index db90f48..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client; - -public interface FileClientFactory { - - /** - * 获得文件客户端 - * - * @param configId 配置编号 - * @return 文件客户端 - */ - FileClient getFileClient(Long configId); - - /** - * 创建文件客户端 - * - * @param configId 配置编号 - * @param storage 存储器的枚举 {@link cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum} - * @param config 文件配置 - */ - void createOrUpdateFileClient(Long configId, Integer storage, Config config); - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactoryImpl.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactoryImpl.java deleted file mode 100644 index 980f8a3..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClientFactoryImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ReflectUtil; -import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum; -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * 文件客户端的工厂实现类 - * - * @author 芋道源码 - */ -@Slf4j -public class FileClientFactoryImpl implements FileClientFactory { - - /** - * 文件客户端 Map - * key:配置编号 - */ - private final ConcurrentMap> clients = new ConcurrentHashMap<>(); - - @Override - public FileClient getFileClient(Long configId) { - AbstractFileClient client = clients.get(configId); - if (client == null) { - log.error("[getFileClient][配置编号({}) 找不到客户端]", configId); - } - return client; - } - - @Override - @SuppressWarnings("unchecked") - public void createOrUpdateFileClient(Long configId, Integer storage, Config config) { - AbstractFileClient client = (AbstractFileClient) clients.get(configId); - if (client == null) { - client = this.createFileClient(configId, storage, config); - client.init(); - clients.put(client.getId(), client); - } else { - client.refresh(config); - } - } - - @SuppressWarnings("unchecked") - private AbstractFileClient createFileClient( - Long configId, Integer storage, Config config) { - FileStorageEnum storageEnum = FileStorageEnum.getByStorage(storage); - Assert.notNull(storageEnum, String.format("文件配置(%s) 为空", storageEnum)); - // 创建客户端 - return (AbstractFileClient) ReflectUtil.newInstance(storageEnum.getClientClass(), configId, config); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClient.java deleted file mode 100644 index 1e2c082..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClient.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.db; - -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; - -/** - * 基于 DB 存储的文件客户端的配置类 - * - * @author 芋道源码 - */ -public class DBFileClient extends AbstractFileClient { - - private DBFileContentFrameworkDAO dao; - - public DBFileClient(Long id, DBFileClientConfig config) { - super(id, config); - } - - @Override - protected void doInit() { - } - - @Override - public String upload(byte[] content, String path, String type) { - getDao().insert(getId(), path, content); - // 拼接返回路径 - return super.formatFileUrl(config.getDomain(), path); - } - - @Override - public void delete(String path) { - getDao().delete(getId(), path); - } - - @Override - public byte[] getContent(String path) { - return getDao().selectContent(getId(), path); - } - - private DBFileContentFrameworkDAO getDao() { - // 延迟获取,因为 SpringUtil 初始化太慢 - if (dao == null) { - dao = SpringUtil.getBean(DBFileContentFrameworkDAO.class); - } - return dao; - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClientConfig.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClientConfig.java deleted file mode 100644 index 23367af..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileClientConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.db; - -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import jakarta.validation.constraints.NotEmpty; - -/** - * 基于 DB 存储的文件客户端的配置类 - * - * @author 芋道源码 - */ -@Data -public class DBFileClientConfig implements FileClientConfig { - - /** - * 自定义域名 - */ - @NotEmpty(message = "domain 不能为空") - @URL(message = "domain 必须是 URL 格式") - private String domain; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileContentFrameworkDAO.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileContentFrameworkDAO.java deleted file mode 100644 index 9423e06..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/db/DBFileContentFrameworkDAO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.db; - -/** - * 文件内容 Framework DAO 接口 - * - * @author 芋道源码 - */ -public interface DBFileContentFrameworkDAO { - - /** - * 插入文件内容 - * - * @param configId 配置编号 - * @param path 路径 - * @param content 内容 - */ - void insert(Long configId, String path, byte[] content); - - /** - * 删除文件内容 - * - * @param configId 配置编号 - * @param path 路径 - */ - void delete(Long configId, String path); - - /** - * 获得文件内容 - * - * @param configId 配置编号 - * @param path 路径 - * @return 内容 - */ - byte[] selectContent(Long configId, String path); - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClient.java deleted file mode 100644 index 796044f..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClient.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.ftp; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.ftp.Ftp; -import cn.hutool.extra.ftp.FtpException; -import cn.hutool.extra.ftp.FtpMode; -import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; - -/** - * Ftp 文件客户端 - * - * @author 芋道源码 - */ -public class FtpFileClient extends AbstractFileClient { - - private Ftp ftp; - - public FtpFileClient(Long id, FtpFileClientConfig config) { - super(id, config); - } - - @Override - protected void doInit() { - // 把配置的 \ 替换成 /, 如果路径配置 \a\test, 替换成 /a/test, 替换方法已经处理 null 情况 - config.setBasePath(StrUtil.replace(config.getBasePath(), StrUtil.BACKSLASH, StrUtil.SLASH)); - // ftp的路径是 / 结尾 - if (!config.getBasePath().endsWith(StrUtil.SLASH)) { - config.setBasePath(config.getBasePath() + StrUtil.SLASH); - } - // 初始化 Ftp 对象 - this.ftp = new Ftp(config.getHost(), config.getPort(), config.getUsername(), config.getPassword(), - CharsetUtil.CHARSET_UTF_8, null, null, FtpMode.valueOf(config.getMode())); - } - - @Override - public String upload(byte[] content, String path, String type) { - // 执行写入 - String filePath = getFilePath(path); - String fileName = FileUtil.getName(filePath); - String dir = StrUtil.removeSuffix(filePath, fileName); - ftp.reconnectIfTimeout(); - boolean success = ftp.upload(dir, fileName, new ByteArrayInputStream(content)); - if (!success) { - throw new FtpException(StrUtil.format("上传文件到目标目录 ({}) 失败", filePath)); - } - // 拼接返回路径 - return super.formatFileUrl(config.getDomain(), path); - } - - @Override - public void delete(String path) { - String filePath = getFilePath(path); - ftp.reconnectIfTimeout(); - ftp.delFile(filePath); - } - - @Override - public byte[] getContent(String path) { - String filePath = getFilePath(path); - String fileName = FileUtil.getName(filePath); - String dir = StrUtil.removeSuffix(filePath, fileName); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ftp.reconnectIfTimeout(); - ftp.download(dir, fileName, out); - return out.toByteArray(); - } - - private String getFilePath(String path) { - return config.getBasePath() + path; - } - -} \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientConfig.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientConfig.java deleted file mode 100644 index a442ddd..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientConfig.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.ftp; - -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -/** - * Ftp 文件客户端的配置类 - * - * @author 芋道源码 - */ -@Data -public class FtpFileClientConfig implements FileClientConfig { - - /** - * 基础路径 - */ - @NotEmpty(message = "基础路径不能为空") - private String basePath; - - /** - * 自定义域名 - */ - @NotEmpty(message = "domain 不能为空") - @URL(message = "domain 必须是 URL 格式") - private String domain; - - /** - * 主机地址 - */ - @NotEmpty(message = "host 不能为空") - private String host; - /** - * 主机端口 - */ - @NotNull(message = "port 不能为空") - private Integer port; - /** - * 用户名 - */ - @NotEmpty(message = "用户名不能为空") - private String username; - /** - * 密码 - */ - @NotEmpty(message = "密码不能为空") - private String password; - /** - * 连接模式 - * - * 使用 {@link cn.hutool.extra.ftp.FtpMode} 对应的字符串 - */ - @NotEmpty(message = "连接模式不能为空") - private String mode; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClient.java deleted file mode 100644 index cac13f1..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClient.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.local; - -import cn.hutool.core.io.FileUtil; -import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; - -import java.io.File; - -/** - * 本地文件客户端 - * - * @author 芋道源码 - */ -public class LocalFileClient extends AbstractFileClient { - - public LocalFileClient(Long id, LocalFileClientConfig config) { - super(id, config); - } - - @Override - protected void doInit() { - // 补全风格。例如说 Linux 是 /,Windows 是 \ - if (!config.getBasePath().endsWith(File.separator)) { - config.setBasePath(config.getBasePath() + File.separator); - } - } - - @Override - public String upload(byte[] content, String path, String type) { - // 执行写入 - String filePath = getFilePath(path); - FileUtil.writeBytes(content, filePath); - // 拼接返回路径 - return super.formatFileUrl(config.getDomain(), path); - } - - @Override - public void delete(String path) { - String filePath = getFilePath(path); - FileUtil.del(filePath); - } - - @Override - public byte[] getContent(String path) { - String filePath = getFilePath(path); - return FileUtil.readBytes(filePath); - } - - private String getFilePath(String path) { - return config.getBasePath() + path; - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientConfig.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientConfig.java deleted file mode 100644 index 1d54eeb..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.local; - -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import jakarta.validation.constraints.NotEmpty; - -/** - * 本地文件客户端的配置类 - * - * @author 芋道源码 - */ -@Data -public class LocalFileClientConfig implements FileClientConfig { - - /** - * 基础路径 - */ - @NotEmpty(message = "基础路径不能为空") - private String basePath; - - /** - * 自定义域名 - */ - @NotEmpty(message = "domain 不能为空") - @URL(message = "domain 必须是 URL 格式") - private String domain; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/FilePresignedUrlRespDTO.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/FilePresignedUrlRespDTO.java deleted file mode 100644 index 6048494..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/FilePresignedUrlRespDTO.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.s3; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 文件预签名地址 Response DTO - * - * @author owen - */ -@Data -@AllArgsConstructor -@NoArgsConstructor -public class FilePresignedUrlRespDTO { - - /** - * 文件上传 URL(用于上传) - * - * 例如说: - */ - private String uploadUrl; - - /** - * 文件 URL(用于读取、下载等) - */ - private String url; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java deleted file mode 100644 index e7b470b..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java +++ /dev/null @@ -1,134 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.s3; - -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.HttpUtil; -import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; -import io.minio.*; -import io.minio.http.Method; - -import java.io.ByteArrayInputStream; -import java.util.concurrent.TimeUnit; - -import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_ALIYUN; -import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_TENCENT; - -/** - * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务 - *

- * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库 - * - * @author 芋道源码 - */ -public class S3FileClient extends AbstractFileClient { - - private MinioClient client; - - public S3FileClient(Long id, S3FileClientConfig config) { - super(id, config); - } - - @Override - protected void doInit() { - // 补全 domain - if (StrUtil.isEmpty(config.getDomain())) { - config.setDomain(buildDomain()); - } - // 初始化客户端 - client = MinioClient.builder() - .endpoint(buildEndpointURL()) // Endpoint URL - .region(buildRegion()) // Region - .credentials(config.getAccessKey(), config.getAccessSecret()) // 认证密钥 - .build(); - } - - /** - * 基于 endpoint 构建调用云服务的 URL 地址 - * - * @return URI 地址 - */ - private String buildEndpointURL() { - // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO - if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { - return config.getEndpoint(); - } - return StrUtil.format("https://{}", config.getEndpoint()); - } - - /** - * 基于 bucket + endpoint 构建访问的 Domain 地址 - * - * @return Domain 地址 - */ - private String buildDomain() { - // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO - if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { - return StrUtil.format("{}/{}", config.getEndpoint(), config.getBucket()); - } - // 阿里云、腾讯云、华为云都适合。七牛云比较特殊,必须有自定义域名 - return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint()); - } - - /** - * 基于 bucket 构建 region 地区 - * - * @return region 地区 - */ - private String buildRegion() { - // 阿里云必须有 region,否则会报错 - if (config.getEndpoint().contains(ENDPOINT_ALIYUN)) { - return StrUtil.subBefore(config.getEndpoint(), '.', false) - .replaceAll("-internal", "")// 去除内网 Endpoint 的后缀 - .replaceAll("https://", ""); - } - // 腾讯云必须有 region,否则会报错 - if (config.getEndpoint().contains(ENDPOINT_TENCENT)) { - return StrUtil.subAfter(config.getEndpoint(), "cos.", false) - .replaceAll("." + ENDPOINT_TENCENT, ""); // 去除 Endpoint - } - return null; - } - - @Override - public String upload(byte[] content, String path, String type) throws Exception { - // 执行上传 - client.putObject(PutObjectArgs.builder() - .bucket(config.getBucket()) // bucket 必须传递 - .contentType(type) - .object(path) // 相对路径作为 key - .stream(new ByteArrayInputStream(content), content.length, -1) // 文件内容 - .build()); - // 拼接返回路径 - return config.getDomain() + "/" + path; - } - - @Override - public void delete(String path) throws Exception { - client.removeObject(RemoveObjectArgs.builder() - .bucket(config.getBucket()) // bucket 必须传递 - .object(path) // 相对路径作为 key - .build()); - } - - @Override - public byte[] getContent(String path) throws Exception { - GetObjectResponse response = client.getObject(GetObjectArgs.builder() - .bucket(config.getBucket()) // bucket 必须传递 - .object(path) // 相对路径作为 key - .build()); - return IoUtil.readBytes(response); - } - - @Override - public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception { - String uploadUrl = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() - .method(Method.PUT) - .bucket(config.getBucket()) - .object(path) - .expiry(10, TimeUnit.MINUTES) // 过期时间(秒数)取值范围:1 秒 ~ 7 天 - .build() - ); - return new FilePresignedUrlRespDTO(uploadUrl, config.getDomain() + "/" + path); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java deleted file mode 100644 index b525839..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.s3; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotNull; - -/** - * S3 文件客户端的配置类 - * - * @author 芋道源码 - */ -@Data -public class S3FileClientConfig implements FileClientConfig { - - public static final String ENDPOINT_QINIU = "qiniucs.com"; - public static final String ENDPOINT_ALIYUN = "aliyuncs.com"; - public static final String ENDPOINT_TENCENT = "myqcloud.com"; - - /** - * 节点地址 - * 1. MinIO:https://www.iocoder.cn/Spring-Boot/MinIO 。例如说,http://127.0.0.1:9000 - * 2. 阿里云:https://help.aliyun.com/document_detail/31837.html - * 3. 腾讯云:https://cloud.tencent.com/document/product/436/6224 - * 4. 七牛云:https://developer.qiniu.com/kodo/4088/s3-access-domainname - * 5. 华为云:https://developer.huaweicloud.com/endpoint?OBS - */ - @NotNull(message = "endpoint 不能为空") - private String endpoint; - /** - * 自定义域名 - * 1. MinIO:通过 Nginx 配置 - * 2. 阿里云:https://help.aliyun.com/document_detail/31836.html - * 3. 腾讯云:https://cloud.tencent.com/document/product/436/11142 - * 4. 七牛云:https://developer.qiniu.com/kodo/8556/set-the-custom-source-domain-name - * 5. 华为云:https://support.huaweicloud.com/usermanual-obs/obs_03_0032.html - */ - @URL(message = "domain 必须是 URL 格式") - private String domain; - /** - * 存储 Bucket - */ - @NotNull(message = "bucket 不能为空") - private String bucket; - - /** - * 访问 Key - * 1. MinIO:https://www.iocoder.cn/Spring-Boot/MinIO - * 2. 阿里云:https://ram.console.aliyun.com/manage/ak - * 3. 腾讯云:https://console.cloud.tencent.com/cam/capi - * 4. 七牛云:https://portal.qiniu.com/user/key - * 5. 华为云:https://support.huaweicloud.com/qs-obs/obs_qs_0005.html - */ - @NotNull(message = "accessKey 不能为空") - private String accessKey; - /** - * 访问 Secret - */ - @NotNull(message = "accessSecret 不能为空") - private String accessSecret; - - @SuppressWarnings("RedundantIfStatement") - @AssertTrue(message = "domain 不能为空") - @JsonIgnore - public boolean isDomainValid() { - // 如果是七牛,必须带有 domain - if (StrUtil.contains(endpoint, ENDPOINT_QINIU) && StrUtil.isEmpty(domain)) { - return false; - } - return true; - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClient.java deleted file mode 100644 index facddce..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClient.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.sftp; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.extra.ssh.Sftp; -import cn.iocoder.yudao.framework.common.util.io.FileUtils; -import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; - -import java.io.File; - -/** - * Sftp 文件客户端 - * - * @author 芋道源码 - */ -public class SftpFileClient extends AbstractFileClient { - - private Sftp sftp; - - public SftpFileClient(Long id, SftpFileClientConfig config) { - super(id, config); - } - - @Override - protected void doInit() { - // 补全风格。例如说 Linux 是 /,Windows 是 \ - if (!config.getBasePath().endsWith(File.separator)) { - config.setBasePath(config.getBasePath() + File.separator); - } - // 初始化 Ftp 对象 - this.sftp = new Sftp(config.getHost(), config.getPort(), config.getUsername(), config.getPassword()); - } - - @Override - public String upload(byte[] content, String path, String type) { - // 执行写入 - String filePath = getFilePath(path); - File file = FileUtils.createTempFile(content); - sftp.upload(filePath, file); - // 拼接返回路径 - return super.formatFileUrl(config.getDomain(), path); - } - - @Override - public void delete(String path) { - String filePath = getFilePath(path); - sftp.delFile(filePath); - } - - @Override - public byte[] getContent(String path) { - String filePath = getFilePath(path); - File destFile = FileUtils.createTempFile(); - sftp.download(filePath, destFile); - return FileUtil.readBytes(destFile); - } - - private String getFilePath(String path) { - return config.getBasePath() + path; - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientConfig.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientConfig.java deleted file mode 100644 index eea5924..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientConfig.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.sftp; - -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -/** - * Sftp 文件客户端的配置类 - * - * @author 芋道源码 - */ -@Data -public class SftpFileClientConfig implements FileClientConfig { - - /** - * 基础路径 - */ - @NotEmpty(message = "基础路径不能为空") - private String basePath; - - /** - * 自定义域名 - */ - @NotEmpty(message = "domain 不能为空") - @URL(message = "domain 必须是 URL 格式") - private String domain; - - /** - * 主机地址 - */ - @NotEmpty(message = "host 不能为空") - private String host; - /** - * 主机端口 - */ - @NotNull(message = "port 不能为空") - private Integer port; - /** - * 用户名 - */ - @NotEmpty(message = "用户名不能为空") - private String username; - /** - * 密码 - */ - @NotEmpty(message = "密码不能为空") - private String password; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/enums/FileStorageEnum.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/enums/FileStorageEnum.java deleted file mode 100644 index 03c6ed8..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/enums/FileStorageEnum.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.enums; - -import cn.hutool.core.util.ArrayUtil; -import cn.iocoder.yudao.framework.file.core.client.FileClient; -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; -import cn.iocoder.yudao.framework.file.core.client.db.DBFileClient; -import cn.iocoder.yudao.framework.file.core.client.db.DBFileClientConfig; -import cn.iocoder.yudao.framework.file.core.client.ftp.FtpFileClient; -import cn.iocoder.yudao.framework.file.core.client.ftp.FtpFileClientConfig; -import cn.iocoder.yudao.framework.file.core.client.local.LocalFileClient; -import cn.iocoder.yudao.framework.file.core.client.local.LocalFileClientConfig; -import cn.iocoder.yudao.framework.file.core.client.s3.S3FileClient; -import cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig; -import cn.iocoder.yudao.framework.file.core.client.sftp.SftpFileClient; -import cn.iocoder.yudao.framework.file.core.client.sftp.SftpFileClientConfig; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 文件存储器枚举 - * - * @author 芋道源码 - */ -@AllArgsConstructor -@Getter -public enum FileStorageEnum { - - DB(1, DBFileClientConfig.class, DBFileClient.class), - - LOCAL(10, LocalFileClientConfig.class, LocalFileClient.class), - FTP(11, FtpFileClientConfig.class, FtpFileClient.class), - SFTP(12, SftpFileClientConfig.class, SftpFileClient.class), - - S3(20, S3FileClientConfig.class, S3FileClient.class), - ; - - /** - * 存储器 - */ - private final Integer storage; - - /** - * 配置类 - */ - private final Class configClass; - /** - * 客户端类 - */ - private final Class clientClass; - - public static FileStorageEnum getByStorage(Integer storage) { - return ArrayUtil.firstMatch(o -> o.getStorage().equals(storage), values()); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/utils/FileTypeUtils.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/utils/FileTypeUtils.java deleted file mode 100644 index 8b99227..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/utils/FileTypeUtils.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.utils; - -import com.alibaba.ttl.TransmittableThreadLocal; -import lombok.SneakyThrows; -import org.apache.tika.Tika; - -/** - * 文件类型 Utils - * - * @author 芋道源码 - */ -public class FileTypeUtils { - - private static final ThreadLocal TIKA = TransmittableThreadLocal.withInitial(Tika::new); - - /** - * 获得文件的 mineType,对于doc,jar等文件会有误差 - * - * @param data 文件内容 - * @return mineType 无法识别时会返回“application/octet-stream” - */ - @SneakyThrows - public static String getMineType(byte[] data) { - return TIKA.get().detect(data); - } - - /** - * 已知文件名,获取文件类型,在某些情况下比通过字节数组准确,例如使用jar文件时,通过名字更为准确 - * - * @param name 文件名 - * @return mineType 无法识别时会返回“application/octet-stream” - */ - public static String getMineType(String name) { - return TIKA.get().detect(name); - } - - /** - * 在拥有文件和数据的情况下,最好使用此方法,最为准确 - * - * @param data 文件内容 - * @param name 文件名 - * @return mineType 无法识别时会返回“application/octet-stream” - */ - public static String getMineType(byte[] data, String name) { - return TIKA.get().detect(data, name); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/yudao-framework/yudao-spring-boot-starter-file/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 9e5f422..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -cn.iocoder.yudao.framework.file.config.YudaoFileAutoConfiguration \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/config/package-info.java b/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/config/package-info.java deleted file mode 100644 index 113f3e5..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/config/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位,避免 package 无法提交到 Git 仓库 - */ -package cn.iocoder.yudao.framework.file.config; diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientTest.java b/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientTest.java deleted file mode 100644 index 619e52d..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.ftp; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.extra.ftp.FtpMode; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -public class FtpFileClientTest { - - @Test - @Disabled - public void test() { - // 创建客户端 - FtpFileClientConfig config = new FtpFileClientConfig(); - config.setDomain("http://127.0.0.1:48080"); - config.setBasePath("/home/ftp"); - config.setHost("kanchai.club"); - config.setPort(221); - config.setUsername(""); - config.setPassword(""); - config.setMode(FtpMode.Passive.name()); - FtpFileClient client = new FtpFileClient(0L, config); - client.init(); - // 上传文件 - String path = IdUtil.fastSimpleUUID() + ".jpg"; - byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); - String fullPath = client.upload(content, path, "image/jpeg"); - System.out.println("访问地址:" + fullPath); - if (false) { - byte[] bytes = client.getContent(path); - System.out.println("文件内容:" + bytes); - } - if (false) { - client.delete(path); - } - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientTest.java b/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientTest.java deleted file mode 100644 index d48609b..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.local; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.IdUtil; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -public class LocalFileClientTest { - - @Test - @Disabled - public void test() { - // 创建客户端 - LocalFileClientConfig config = new LocalFileClientConfig(); - config.setDomain("http://127.0.0.1:48080"); - config.setBasePath("/Users/yunai/file_test"); - LocalFileClient client = new LocalFileClient(0L, config); - client.init(); - // 上传文件 - String path = IdUtil.fastSimpleUUID() + ".jpg"; - byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); - String fullPath = client.upload(content, path, "image/jpeg"); - System.out.println("访问地址:" + fullPath); - client.delete(path); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientTest.java b/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientTest.java deleted file mode 100644 index cbd41ab..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.s3; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.IdUtil; -import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import jakarta.validation.Validation; - -public class S3FileClientTest { - - @Test - @Disabled // MinIO,如果要集成测试,可以注释本行 - public void testMinIO() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 - config.setAccessKey("admin"); - config.setAccessSecret("password"); - config.setBucket("yudaoyuanma"); - config.setDomain(null); - // 默认 9000 endpoint - config.setEndpoint("http://127.0.0.1:9000"); - - // 执行上传 - testExecuteUpload(config); - } - - @Test - @Disabled // 阿里云 OSS,如果要集成测试,可以注释本行 - public void testAliyun() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 - config.setAccessKey(System.getenv("ALIYUN_ACCESS_KEY")); - config.setAccessSecret(System.getenv("ALIYUN_SECRET_KEY")); - config.setBucket("yunai-aoteman"); - config.setDomain(null); // 如果有自定义域名,则可以设置。http://ali-oss.iocoder.cn - // 默认北京的 endpoint - config.setEndpoint("oss-cn-beijing.aliyuncs.com"); - - // 执行上传 - testExecuteUpload(config); - } - - @Test - @Disabled // 腾讯云 COS,如果要集成测试,可以注释本行 - public void testQCloud() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 - config.setAccessKey(System.getenv("QCLOUD_ACCESS_KEY")); - config.setAccessSecret(System.getenv("QCLOUD_SECRET_KEY")); - config.setBucket("aoteman-1255880240"); - config.setDomain(null); // 如果有自定义域名,则可以设置。http://tengxun-oss.iocoder.cn - // 默认上海的 endpoint - config.setEndpoint("cos.ap-shanghai.myqcloud.com"); - - // 执行上传 - testExecuteUpload(config); - } - - @Test - @Disabled // 七牛云存储,如果要集成测试,可以注释本行 - public void testQiniu() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 -// config.setAccessKey(System.getenv("QINIU_ACCESS_KEY")); -// config.setAccessSecret(System.getenv("QINIU_SECRET_KEY")); - config.setAccessKey("b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8"); - config.setAccessSecret("kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP"); - config.setBucket("ruoyi-vue-pro"); - config.setDomain("http://test.yudao.iocoder.cn"); // 如果有自定义域名,则可以设置。http://static.yudao.iocoder.cn - // 默认上海的 endpoint - config.setEndpoint("s3-cn-south-1.qiniucs.com"); - - // 执行上传 - testExecuteUpload(config); - } - - @Test - @Disabled // 华为云存储,如果要集成测试,可以注释本行 - public void testHuaweiCloud() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 -// config.setAccessKey(System.getenv("HUAWEI_CLOUD_ACCESS_KEY")); -// config.setAccessSecret(System.getenv("HUAWEI_CLOUD_SECRET_KEY")); - config.setBucket("yudao"); - config.setDomain(null); // 如果有自定义域名,则可以设置。 - // 默认上海的 endpoint - config.setEndpoint("obs.cn-east-3.myhuaweicloud.com"); - - // 执行上传 - testExecuteUpload(config); - } - - private void testExecuteUpload(S3FileClientConfig config) throws Exception { - // 校验配置 - ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config); - // 创建 Client - S3FileClient client = new S3FileClient(0L, config); - client.init(); - // 上传文件 - String path = IdUtil.fastSimpleUUID() + ".jpg"; - byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); - String fullPath = client.upload(content, path, "image/jpeg"); - System.out.println("访问地址:" + fullPath); - // 读取文件 - if (true) { - byte[] bytes = client.getContent(path); - System.out.println("文件内容:" + bytes.length); - } - // 删除文件 - if (false) { - client.delete(path); - } - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientTest.java b/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientTest.java deleted file mode 100644 index 4785c0d..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.framework.file.core.client.sftp; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.IdUtil; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -public class SftpFileClientTest { - - @Test - @Disabled - public void test() { - // 创建客户端 - SftpFileClientConfig config = new SftpFileClientConfig(); - config.setDomain("http://127.0.0.1:48080"); - config.setBasePath("/home/ftp"); - config.setHost("kanchai.club"); - config.setPort(222); - config.setUsername(""); - config.setPassword(""); - SftpFileClient client = new SftpFileClient(0L, config); - client.init(); - // 上传文件 - String path = IdUtil.fastSimpleUUID() + ".jpg"; - byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); - String fullPath = client.upload(content, path, "image/jpeg"); - System.out.println("访问地址:" + fullPath); - if (false) { - byte[] bytes = client.getContent(path); - System.out.println("文件内容:" + bytes); - } - if (false) { - client.delete(path); - } - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/enums/package-info.java b/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/enums/package-info.java deleted file mode 100644 index e1da5db..0000000 --- a/yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/enums/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位,避免 package 无法提交到 Git 仓库 - */ -package cn.iocoder.yudao.framework.file.core.enums; diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/test/resources/file/erweima.jpg b/yudao-framework/yudao-spring-boot-starter-file/src/test/resources/file/erweima.jpg deleted file mode 100644 index 1447283..0000000 Binary files a/yudao-framework/yudao-spring-boot-starter-file/src/test/resources/file/erweima.jpg and /dev/null differ diff --git a/yudao-module-infra/yudao-module-infra-biz/pom.xml b/yudao-module-infra/yudao-module-infra-biz/pom.xml index 4c6cd40..d030846 100644 --- a/yudao-module-infra/yudao-module-infra-biz/pom.xml +++ b/yudao-module-infra/yudao-module-infra-biz/pom.xml @@ -117,8 +117,21 @@ - cn.iocoder.boot - yudao-spring-boot-starter-file + commons-net + commons-net + + + com.jcraft + jsch + + + io.minio + minio + + + + org.apache.tika + tika-core diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java index e3bb6fe..e344f82 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.infra.controller.admin.file.vo.config; -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java index c30a9d5..ac2d4d0 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java @@ -1,12 +1,20 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.file; -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; -import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; +import com.fasterxml.jackson.core.type.TypeReference; import lombok.*; /** @@ -52,7 +60,42 @@ public class FileConfigDO extends BaseDO { /** * 支付渠道配置 */ - @TableField(typeHandler = JacksonTypeHandler.class) + @TableField(typeHandler = FileClientConfigTypeHandler.class) private FileClientConfig config; + public static class FileClientConfigTypeHandler extends AbstractJsonTypeHandler { + + @Override + protected Object parse(String json) { + FileClientConfig config = JsonUtils.parseObjectQuietly(json, new TypeReference<>() {}); + if (config != null) { + return config; + } + + // 兼容老版本的包路径 + String className = JsonUtils.parseObject(json, "@class", String.class); + className = StrUtil.subAfter(className, ".", true); + switch (className) { + case "DBFileClientConfig": + return JsonUtils.parseObject2(json, DBFileClientConfig.class); + case "FtpFileClientConfig": + return JsonUtils.parseObject2(json, FtpFileClientConfig.class); + case "LocalFileClientConfig": + return JsonUtils.parseObject2(json, LocalFileClientConfig.class); + case "SftpFileClientConfig": + return JsonUtils.parseObject2(json, SftpFileClientConfig.class); + case "S3FileClientConfig": + return JsonUtils.parseObject2(json, S3FileClientConfig.class); + default: + throw new IllegalArgumentException("未知的 FileClientConfig 类型:" + json); + } + } + + @Override + protected String toJson(Object obj) { + return JsonUtils.toJsonString(obj); + } + + } + } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java index e19a1ce..eda8a72 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.file; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClient; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; @@ -10,7 +11,7 @@ import lombok.*; /** * 文件内容表 * - * 专门用于存储 {@link cn.iocoder.yudao.framework.file.core.client.db.DBFileClient} 的文件内容 + * 专门用于存储 {@link DBFileClient} 的文件内容 * * @author 芋道源码 */ diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentDAOImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentDAOImpl.java deleted file mode 100644 index b90bed6..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentDAOImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.file; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.file.core.client.db.DBFileContentFrameworkDAO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileContentDO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.springframework.stereotype.Repository; - -import jakarta.annotation.Resource; -import java.util.List; -import java.util.Optional; - -@Repository -public class FileContentDAOImpl implements DBFileContentFrameworkDAO { - - @Resource - private FileContentMapper fileContentMapper; - - @Override - public void insert(Long configId, String path, byte[] content) { - FileContentDO entity = new FileContentDO().setConfigId(configId) - .setPath(path).setContent(content); - fileContentMapper.insert(entity); - } - - @Override - public void delete(Long configId, String path) { - fileContentMapper.delete(buildQuery(configId, path)); - } - - @Override - public byte[] selectContent(Long configId, String path) { - List list = fileContentMapper.selectList( - buildQuery(configId, path).select(FileContentDO::getContent).orderByDesc(FileContentDO::getId)); - return Optional.ofNullable(CollUtil.getFirst(list)) - .map(FileContentDO::getContent) - .orElse(null); - } - - private LambdaQueryWrapper buildQuery(Long configId, String path) { - return new LambdaQueryWrapper() - .eq(FileContentDO::getConfigId, configId) - .eq(FileContentDO::getPath, path); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java index 501979d..3d6ec51 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java @@ -1,9 +1,25 @@ package cn.iocoder.yudao.module.infra.dal.mysql.file; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileContentDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + @Mapper public interface FileContentMapper extends BaseMapper { + + default void deleteByConfigIdAndPath(Long configId, String path) { + this.delete(new LambdaQueryWrapper() + .eq(FileContentDO::getConfigId, configId) + .eq(FileContentDO::getPath, path)); + } + + default List selectListByConfigIdAndPath(Long configId, String path) { + return selectList(new LambdaQueryWrapper() + .eq(FileContentDO::getConfigId, configId) + .eq(FileContentDO::getPath, path)); + } + } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java new file mode 100644 index 0000000..e0d3f8e --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.infra.framework.file.config; + +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactoryImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 文件配置类 + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +public class YudaoFileAutoConfiguration { + + @Bean + public FileClientFactory fileClientFactory() { + return new FileClientFactoryImpl(); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java new file mode 100644 index 0000000..3c7883b --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +/** + * 文件客户端的抽象类,提供模板方法,减少子类的冗余代码 + * + * @author 芋道源码 + */ +@Slf4j +public abstract class AbstractFileClient implements FileClient { + + /** + * 配置编号 + */ + private final Long id; + /** + * 文件配置 + */ + protected Config config; + + public AbstractFileClient(Long id, Config config) { + this.id = id; + this.config = config; + } + + /** + * 初始化 + */ + public final void init() { + doInit(); + log.debug("[init][配置({}) 初始化完成]", config); + } + + /** + * 自定义初始化 + */ + protected abstract void doInit(); + + public final void refresh(Config config) { + // 判断是否更新 + if (config.equals(this.config)) { + return; + } + log.info("[refresh][配置({})发生变化,重新初始化]", config); + this.config = config; + // 初始化 + this.init(); + } + + @Override + public Long getId() { + return id; + } + + /** + * 格式化文件的 URL 访问地址 + * 使用场景:local、ftp、db,通过 FileController 的 getFile 来获取文件内容 + * + * @param domain 自定义域名 + * @param path 文件路径 + * @return URL 访问地址 + */ + protected String formatFileUrl(String domain, String path) { + return StrUtil.format("{}/admin-api/infra/file/{}/get/{}", domain, getId(), path); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java new file mode 100644 index 0000000..053b3c5 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client; + +import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO; + +/** + * 文件客户端 + * + * @author 芋道源码 + */ +public interface FileClient { + + /** + * 获得客户端编号 + * + * @return 客户端编号 + */ + Long getId(); + + /** + * 上传文件 + * + * @param content 文件流 + * @param path 相对路径 + * @return 完整路径,即 HTTP 访问地址 + * @throws Exception 上传文件时,抛出 Exception 异常 + */ + String upload(byte[] content, String path, String type) throws Exception; + + /** + * 删除文件 + * + * @param path 相对路径 + * @throws Exception 删除文件时,抛出 Exception 异常 + */ + void delete(String path) throws Exception; + + /** + * 获得文件的内容 + * + * @param path 相对路径 + * @return 文件的内容 + */ + byte[] getContent(String path) throws Exception; + + /** + * 获得文件预签名地址 + * + * @param path 相对路径 + * @return 文件预签名地址 + */ + default FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception { + throw new UnsupportedOperationException("不支持的操作"); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java new file mode 100644 index 0000000..ed09275 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * 文件客户端的配置 + * 不同实现的客户端,需要不同的配置,通过子类来定义 + * + * @author 芋道源码 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) +// @JsonTypeInfo 注解的作用,Jackson 多态 +// 1. 序列化到时数据库时,增加 @class 属性。 +// 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 +public interface FileClientConfig { +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java new file mode 100644 index 0000000..e4c84ed --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client; + +import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum; + +public interface FileClientFactory { + + /** + * 获得文件客户端 + * + * @param configId 配置编号 + * @return 文件客户端 + */ + FileClient getFileClient(Long configId); + + /** + * 创建文件客户端 + * + * @param configId 配置编号 + * @param storage 存储器的枚举 {@link FileStorageEnum} + * @param config 文件配置 + */ + void createOrUpdateFileClient(Long configId, Integer storage, Config config); + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java new file mode 100644 index 0000000..2bd5464 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ReflectUtil; +import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * 文件客户端的工厂实现类 + * + * @author 芋道源码 + */ +@Slf4j +public class FileClientFactoryImpl implements FileClientFactory { + + /** + * 文件客户端 Map + * key:配置编号 + */ + private final ConcurrentMap> clients = new ConcurrentHashMap<>(); + + @Override + public FileClient getFileClient(Long configId) { + AbstractFileClient client = clients.get(configId); + if (client == null) { + log.error("[getFileClient][配置编号({}) 找不到客户端]", configId); + } + return client; + } + + @Override + @SuppressWarnings("unchecked") + public void createOrUpdateFileClient(Long configId, Integer storage, Config config) { + AbstractFileClient client = (AbstractFileClient) clients.get(configId); + if (client == null) { + client = this.createFileClient(configId, storage, config); + client.init(); + clients.put(client.getId(), client); + } else { + client.refresh(config); + } + } + + @SuppressWarnings("unchecked") + private AbstractFileClient createFileClient( + Long configId, Integer storage, Config config) { + FileStorageEnum storageEnum = FileStorageEnum.getByStorage(storage); + Assert.notNull(storageEnum, String.format("文件配置(%s) 为空", storageEnum)); + // 创建客户端 + return (AbstractFileClient) ReflectUtil.newInstance(storageEnum.getClientClass(), configId, config); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java new file mode 100644 index 0000000..0a050d3 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.db; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileContentDO; +import cn.iocoder.yudao.module.infra.dal.mysql.file.FileContentMapper; +import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; + +import java.util.Comparator; +import java.util.List; + +/** + * 基于 DB 存储的文件客户端的配置类 + * + * @author 芋道源码 + */ +public class DBFileClient extends AbstractFileClient { + + private FileContentMapper fileContentMapper; + + public DBFileClient(Long id, DBFileClientConfig config) { + super(id, config); + } + + @Override + protected void doInit() { + fileContentMapper = SpringUtil.getBean(FileContentMapper.class); + } + + @Override + public String upload(byte[] content, String path, String type) { + FileContentDO contentDO = new FileContentDO().setConfigId(getId()) + .setPath(path).setContent(content); + fileContentMapper.insert(contentDO); + // 拼接返回路径 + return super.formatFileUrl(config.getDomain(), path); + } + + @Override + public void delete(String path) { + fileContentMapper.deleteByConfigIdAndPath(getId(), path); + } + + @Override + public byte[] getContent(String path) { + List list = fileContentMapper.selectListByConfigIdAndPath(getId(), path); + if (CollUtil.isEmpty(list)) { + return null; + } + // 排序后,拿 id 最大的,即最后上传的 + list.sort(Comparator.comparing(FileContentDO::getId)); + return CollUtil.getLast(list).getContent(); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java new file mode 100644 index 0000000..1f619df --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.db; + +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.constraints.NotEmpty; + +/** + * 基于 DB 存储的文件客户端的配置类 + * + * @author 芋道源码 + */ +@Data +public class DBFileClientConfig implements FileClientConfig { + + /** + * 自定义域名 + */ + @NotEmpty(message = "domain 不能为空") + @URL(message = "domain 必须是 URL 格式") + private String domain; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java new file mode 100644 index 0000000..062d838 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.ftp; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.ftp.Ftp; +import cn.hutool.extra.ftp.FtpException; +import cn.hutool.extra.ftp.FtpMode; +import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +/** + * Ftp 文件客户端 + * + * @author 芋道源码 + */ +public class FtpFileClient extends AbstractFileClient { + + private Ftp ftp; + + public FtpFileClient(Long id, FtpFileClientConfig config) { + super(id, config); + } + + @Override + protected void doInit() { + // 把配置的 \ 替换成 /, 如果路径配置 \a\test, 替换成 /a/test, 替换方法已经处理 null 情况 + config.setBasePath(StrUtil.replace(config.getBasePath(), StrUtil.BACKSLASH, StrUtil.SLASH)); + // ftp的路径是 / 结尾 + if (!config.getBasePath().endsWith(StrUtil.SLASH)) { + config.setBasePath(config.getBasePath() + StrUtil.SLASH); + } + // 初始化 Ftp 对象 + this.ftp = new Ftp(config.getHost(), config.getPort(), config.getUsername(), config.getPassword(), + CharsetUtil.CHARSET_UTF_8, null, null, FtpMode.valueOf(config.getMode())); + } + + @Override + public String upload(byte[] content, String path, String type) { + // 执行写入 + String filePath = getFilePath(path); + String fileName = FileUtil.getName(filePath); + String dir = StrUtil.removeSuffix(filePath, fileName); + ftp.reconnectIfTimeout(); + boolean success = ftp.upload(dir, fileName, new ByteArrayInputStream(content)); + if (!success) { + throw new FtpException(StrUtil.format("上传文件到目标目录 ({}) 失败", filePath)); + } + // 拼接返回路径 + return super.formatFileUrl(config.getDomain(), path); + } + + @Override + public void delete(String path) { + String filePath = getFilePath(path); + ftp.reconnectIfTimeout(); + ftp.delFile(filePath); + } + + @Override + public byte[] getContent(String path) { + String filePath = getFilePath(path); + String fileName = FileUtil.getName(filePath); + String dir = StrUtil.removeSuffix(filePath, fileName); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ftp.reconnectIfTimeout(); + ftp.download(dir, fileName, out); + return out.toByteArray(); + } + + private String getFilePath(String path) { + return config.getBasePath() + path; + } + +} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java new file mode 100644 index 0000000..a1b92ff --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.ftp; + +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * Ftp 文件客户端的配置类 + * + * @author 芋道源码 + */ +@Data +public class FtpFileClientConfig implements FileClientConfig { + + /** + * 基础路径 + */ + @NotEmpty(message = "基础路径不能为空") + private String basePath; + + /** + * 自定义域名 + */ + @NotEmpty(message = "domain 不能为空") + @URL(message = "domain 必须是 URL 格式") + private String domain; + + /** + * 主机地址 + */ + @NotEmpty(message = "host 不能为空") + private String host; + /** + * 主机端口 + */ + @NotNull(message = "port 不能为空") + private Integer port; + /** + * 用户名 + */ + @NotEmpty(message = "用户名不能为空") + private String username; + /** + * 密码 + */ + @NotEmpty(message = "密码不能为空") + private String password; + /** + * 连接模式 + * + * 使用 {@link cn.hutool.extra.ftp.FtpMode} 对应的字符串 + */ + @NotEmpty(message = "连接模式不能为空") + private String mode; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java new file mode 100644 index 0000000..a919690 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.local; + +import cn.hutool.core.io.FileUtil; +import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; + +import java.io.File; + +/** + * 本地文件客户端 + * + * @author 芋道源码 + */ +public class LocalFileClient extends AbstractFileClient { + + public LocalFileClient(Long id, LocalFileClientConfig config) { + super(id, config); + } + + @Override + protected void doInit() { + // 补全风格。例如说 Linux 是 /,Windows 是 \ + if (!config.getBasePath().endsWith(File.separator)) { + config.setBasePath(config.getBasePath() + File.separator); + } + } + + @Override + public String upload(byte[] content, String path, String type) { + // 执行写入 + String filePath = getFilePath(path); + FileUtil.writeBytes(content, filePath); + // 拼接返回路径 + return super.formatFileUrl(config.getDomain(), path); + } + + @Override + public void delete(String path) { + String filePath = getFilePath(path); + FileUtil.del(filePath); + } + + @Override + public byte[] getContent(String path) { + String filePath = getFilePath(path); + return FileUtil.readBytes(filePath); + } + + private String getFilePath(String path) { + return config.getBasePath() + path; + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java new file mode 100644 index 0000000..d56d4ef --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.local; + +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.constraints.NotEmpty; + +/** + * 本地文件客户端的配置类 + * + * @author 芋道源码 + */ +@Data +public class LocalFileClientConfig implements FileClientConfig { + + /** + * 基础路径 + */ + @NotEmpty(message = "基础路径不能为空") + private String basePath; + + /** + * 自定义域名 + */ + @NotEmpty(message = "domain 不能为空") + @URL(message = "domain 必须是 URL 格式") + private String domain; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java new file mode 100644 index 0000000..6a1258e --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.s3; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 文件预签名地址 Response DTO + * + * @author owen + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class FilePresignedUrlRespDTO { + + /** + * 文件上传 URL(用于上传) + * + * 例如说: + */ + private String uploadUrl; + + /** + * 文件 URL(用于读取、下载等) + */ + private String url; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java new file mode 100644 index 0000000..67995ca --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java @@ -0,0 +1,131 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.s3; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; +import io.minio.*; +import io.minio.http.Method; + +import java.io.ByteArrayInputStream; +import java.util.concurrent.TimeUnit; + +/** + * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务 + *

+ * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库 + * + * @author 芋道源码 + */ +public class S3FileClient extends AbstractFileClient { + + private MinioClient client; + + public S3FileClient(Long id, S3FileClientConfig config) { + super(id, config); + } + + @Override + protected void doInit() { + // 补全 domain + if (StrUtil.isEmpty(config.getDomain())) { + config.setDomain(buildDomain()); + } + // 初始化客户端 + client = MinioClient.builder() + .endpoint(buildEndpointURL()) // Endpoint URL + .region(buildRegion()) // Region + .credentials(config.getAccessKey(), config.getAccessSecret()) // 认证密钥 + .build(); + } + + /** + * 基于 endpoint 构建调用云服务的 URL 地址 + * + * @return URI 地址 + */ + private String buildEndpointURL() { + // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO + if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { + return config.getEndpoint(); + } + return StrUtil.format("https://{}", config.getEndpoint()); + } + + /** + * 基于 bucket + endpoint 构建访问的 Domain 地址 + * + * @return Domain 地址 + */ + private String buildDomain() { + // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO + if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { + return StrUtil.format("{}/{}", config.getEndpoint(), config.getBucket()); + } + // 阿里云、腾讯云、华为云都适合。七牛云比较特殊,必须有自定义域名 + return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint()); + } + + /** + * 基于 bucket 构建 region 地区 + * + * @return region 地区 + */ + private String buildRegion() { + // 阿里云必须有 region,否则会报错 + if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_ALIYUN)) { + return StrUtil.subBefore(config.getEndpoint(), '.', false) + .replaceAll("-internal", "")// 去除内网 Endpoint 的后缀 + .replaceAll("https://", ""); + } + // 腾讯云必须有 region,否则会报错 + if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_TENCENT)) { + return StrUtil.subAfter(config.getEndpoint(), "cos.", false) + .replaceAll("." + S3FileClientConfig.ENDPOINT_TENCENT, ""); // 去除 Endpoint + } + return null; + } + + @Override + public String upload(byte[] content, String path, String type) throws Exception { + // 执行上传 + client.putObject(PutObjectArgs.builder() + .bucket(config.getBucket()) // bucket 必须传递 + .contentType(type) + .object(path) // 相对路径作为 key + .stream(new ByteArrayInputStream(content), content.length, -1) // 文件内容 + .build()); + // 拼接返回路径 + return config.getDomain() + "/" + path; + } + + @Override + public void delete(String path) throws Exception { + client.removeObject(RemoveObjectArgs.builder() + .bucket(config.getBucket()) // bucket 必须传递 + .object(path) // 相对路径作为 key + .build()); + } + + @Override + public byte[] getContent(String path) throws Exception { + GetObjectResponse response = client.getObject(GetObjectArgs.builder() + .bucket(config.getBucket()) // bucket 必须传递 + .object(path) // 相对路径作为 key + .build()); + return IoUtil.readBytes(response); + } + + @Override + public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception { + String uploadUrl = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() + .method(Method.PUT) + .bucket(config.getBucket()) + .object(path) + .expiry(10, TimeUnit.MINUTES) // 过期时间(秒数)取值范围:1 秒 ~ 7 天 + .build() + ); + return new FilePresignedUrlRespDTO(uploadUrl, config.getDomain() + "/" + path); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java new file mode 100644 index 0000000..872bc0d --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.s3; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; + +/** + * S3 文件客户端的配置类 + * + * @author 芋道源码 + */ +@Data +public class S3FileClientConfig implements FileClientConfig { + + public static final String ENDPOINT_QINIU = "qiniucs.com"; + public static final String ENDPOINT_ALIYUN = "aliyuncs.com"; + public static final String ENDPOINT_TENCENT = "myqcloud.com"; + + /** + * 节点地址 + * 1. MinIO:https://www.iocoder.cn/Spring-Boot/MinIO 。例如说,http://127.0.0.1:9000 + * 2. 阿里云:https://help.aliyun.com/document_detail/31837.html + * 3. 腾讯云:https://cloud.tencent.com/document/product/436/6224 + * 4. 七牛云:https://developer.qiniu.com/kodo/4088/s3-access-domainname + * 5. 华为云:https://developer.huaweicloud.com/endpoint?OBS + */ + @NotNull(message = "endpoint 不能为空") + private String endpoint; + /** + * 自定义域名 + * 1. MinIO:通过 Nginx 配置 + * 2. 阿里云:https://help.aliyun.com/document_detail/31836.html + * 3. 腾讯云:https://cloud.tencent.com/document/product/436/11142 + * 4. 七牛云:https://developer.qiniu.com/kodo/8556/set-the-custom-source-domain-name + * 5. 华为云:https://support.huaweicloud.com/usermanual-obs/obs_03_0032.html + */ + @URL(message = "domain 必须是 URL 格式") + private String domain; + /** + * 存储 Bucket + */ + @NotNull(message = "bucket 不能为空") + private String bucket; + + /** + * 访问 Key + * 1. MinIO:https://www.iocoder.cn/Spring-Boot/MinIO + * 2. 阿里云:https://ram.console.aliyun.com/manage/ak + * 3. 腾讯云:https://console.cloud.tencent.com/cam/capi + * 4. 七牛云:https://portal.qiniu.com/user/key + * 5. 华为云:https://support.huaweicloud.com/qs-obs/obs_qs_0005.html + */ + @NotNull(message = "accessKey 不能为空") + private String accessKey; + /** + * 访问 Secret + */ + @NotNull(message = "accessSecret 不能为空") + private String accessSecret; + + @SuppressWarnings("RedundantIfStatement") + @AssertTrue(message = "domain 不能为空") + @JsonIgnore + public boolean isDomainValid() { + // 如果是七牛,必须带有 domain + if (StrUtil.contains(endpoint, ENDPOINT_QINIU) && StrUtil.isEmpty(domain)) { + return false; + } + return true; + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java new file mode 100644 index 0000000..3ebe782 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.sftp; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.extra.ssh.Sftp; +import cn.iocoder.yudao.framework.common.util.io.FileUtils; +import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; + +import java.io.File; + +/** + * Sftp 文件客户端 + * + * @author 芋道源码 + */ +public class SftpFileClient extends AbstractFileClient { + + private Sftp sftp; + + public SftpFileClient(Long id, SftpFileClientConfig config) { + super(id, config); + } + + @Override + protected void doInit() { + // 补全风格。例如说 Linux 是 /,Windows 是 \ + if (!config.getBasePath().endsWith(File.separator)) { + config.setBasePath(config.getBasePath() + File.separator); + } + // 初始化 Ftp 对象 + this.sftp = new Sftp(config.getHost(), config.getPort(), config.getUsername(), config.getPassword()); + } + + @Override + public String upload(byte[] content, String path, String type) { + // 执行写入 + String filePath = getFilePath(path); + File file = FileUtils.createTempFile(content); + sftp.upload(filePath, file); + // 拼接返回路径 + return super.formatFileUrl(config.getDomain(), path); + } + + @Override + public void delete(String path) { + String filePath = getFilePath(path); + sftp.delFile(filePath); + } + + @Override + public byte[] getContent(String path) { + String filePath = getFilePath(path); + File destFile = FileUtils.createTempFile(); + sftp.download(filePath, destFile); + return FileUtil.readBytes(destFile); + } + + private String getFilePath(String path) { + return config.getBasePath() + path; + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java new file mode 100644 index 0000000..c56d045 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.client.sftp; + +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * Sftp 文件客户端的配置类 + * + * @author 芋道源码 + */ +@Data +public class SftpFileClientConfig implements FileClientConfig { + + /** + * 基础路径 + */ + @NotEmpty(message = "基础路径不能为空") + private String basePath; + + /** + * 自定义域名 + */ + @NotEmpty(message = "domain 不能为空") + @URL(message = "domain 必须是 URL 格式") + private String domain; + + /** + * 主机地址 + */ + @NotEmpty(message = "host 不能为空") + private String host; + /** + * 主机端口 + */ + @NotNull(message = "port 不能为空") + private Integer port; + /** + * 用户名 + */ + @NotEmpty(message = "用户名不能为空") + private String username; + /** + * 密码 + */ + @NotEmpty(message = "密码不能为空") + private String password; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java new file mode 100644 index 0000000..866c43b --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.enums; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 文件存储器枚举 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum FileStorageEnum { + + DB(1, DBFileClientConfig.class, DBFileClient.class), + + LOCAL(10, LocalFileClientConfig.class, LocalFileClient.class), + FTP(11, FtpFileClientConfig.class, FtpFileClient.class), + SFTP(12, SftpFileClientConfig.class, SftpFileClient.class), + + S3(20, S3FileClientConfig.class, S3FileClient.class), + ; + + /** + * 存储器 + */ + private final Integer storage; + + /** + * 配置类 + */ + private final Class configClass; + /** + * 客户端类 + */ + private final Class clientClass; + + public static FileStorageEnum getByStorage(Integer storage) { + return ArrayUtil.firstMatch(o -> o.getStorage().equals(storage), values()); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java new file mode 100644 index 0000000..ea71f58 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.utils; + +import com.alibaba.ttl.TransmittableThreadLocal; +import lombok.SneakyThrows; +import org.apache.tika.Tika; + +/** + * 文件类型 Utils + * + * @author 芋道源码 + */ +public class FileTypeUtils { + + private static final ThreadLocal TIKA = TransmittableThreadLocal.withInitial(Tika::new); + + /** + * 获得文件的 mineType,对于doc,jar等文件会有误差 + * + * @param data 文件内容 + * @return mineType 无法识别时会返回“application/octet-stream” + */ + @SneakyThrows + public static String getMineType(byte[] data) { + return TIKA.get().detect(data); + } + + /** + * 已知文件名,获取文件类型,在某些情况下比通过字节数组准确,例如使用jar文件时,通过名字更为准确 + * + * @param name 文件名 + * @return mineType 无法识别时会返回“application/octet-stream” + */ + public static String getMineType(String name) { + return TIKA.get().detect(name); + } + + /** + * 在拥有文件和数据的情况下,最好使用此方法,最为准确 + * + * @param data 文件内容 + * @param name 文件名 + * @return mineType 无法识别时会返回“application/octet-stream” + */ + public static String getMineType(byte[] data, String name) { + return TIKA.get().detect(data, name); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java new file mode 100644 index 0000000..04b6867 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java @@ -0,0 +1,12 @@ +/** + * 文件客户端,支持多种存储器 + * + * 1. local:本地磁盘 + * 2. ftp:FTP 服务器 + * 3. sftp:SFTP 服务器 + * 4. db:数据库 + * 5. s3:支持 S3 协议的云存储服务,例如说 MinIO、阿里云、华为云、腾讯云、七牛云等等 + * + * @author 芋道源码 + */ +package cn.iocoder.yudao.module.infra.framework.file; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java index 0019915..95ae94a 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.infra.service.file; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.file.core.client.FileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java index eda97bf..37e2c46 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java @@ -5,10 +5,10 @@ import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; -import cn.iocoder.yudao.framework.file.core.client.FileClient; -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; -import cn.iocoder.yudao.framework.file.core.client.FileClientFactory; -import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory; +import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO; import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java index f7c4b0b..72c7dec 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java @@ -5,9 +5,9 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.io.FileUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.file.core.client.FileClient; -import cn.iocoder.yudao.framework.file.core.client.s3.FilePresignedUrlRespDTO; -import cn.iocoder.yudao.framework.file.core.utils.FileTypeUtils; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO; +import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/file/erweima.jpg b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/file/erweima.jpg index 1447283..e69de29 100644 Binary files a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/file/erweima.jpg and b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/file/erweima.jpg differ diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java new file mode 100644 index 0000000..b8876f7 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.ftp; + +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.extra.ftp.FtpMode; +import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class FtpFileClientTest { + + @Test + @Disabled + public void test() { + // 创建客户端 + FtpFileClientConfig config = new FtpFileClientConfig(); + config.setDomain("http://127.0.0.1:48080"); + config.setBasePath("/home/ftp"); + config.setHost("kanchai.club"); + config.setPort(221); + config.setUsername(""); + config.setPassword(""); + config.setMode(FtpMode.Passive.name()); + FtpFileClient client = new FtpFileClient(0L, config); + client.init(); + // 上传文件 + String path = IdUtil.fastSimpleUUID() + ".jpg"; + byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); + String fullPath = client.upload(content, path, "image/jpeg"); + System.out.println("访问地址:" + fullPath); + if (false) { + byte[] bytes = client.getContent(path); + System.out.println("文件内容:" + bytes); + } + if (false) { + client.delete(path); + } + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java new file mode 100644 index 0000000..7c622a5 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.local; + +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class LocalFileClientTest { + + @Test + @Disabled + public void test() { + // 创建客户端 + LocalFileClientConfig config = new LocalFileClientConfig(); + config.setDomain("http://127.0.0.1:48080"); + config.setBasePath("/Users/yunai/file_test"); + LocalFileClient client = new LocalFileClient(0L, config); + client.init(); + // 上传文件 + String path = IdUtil.fastSimpleUUID() + ".jpg"; + byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); + String fullPath = client.upload(content, path, "image/jpeg"); + System.out.println("访问地址:" + fullPath); + client.delete(path); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java new file mode 100644 index 0000000..3c40ce2 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java @@ -0,0 +1,118 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.s3; + +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig; +import jakarta.validation.Validation; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class S3FileClientTest { + + @Test + @Disabled // MinIO,如果要集成测试,可以注释本行 + public void testMinIO() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 + config.setAccessKey("admin"); + config.setAccessSecret("password"); + config.setBucket("yudaoyuanma"); + config.setDomain(null); + // 默认 9000 endpoint + config.setEndpoint("http://127.0.0.1:9000"); + + // 执行上传 + testExecuteUpload(config); + } + + @Test + @Disabled // 阿里云 OSS,如果要集成测试,可以注释本行 + public void testAliyun() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 + config.setAccessKey(System.getenv("ALIYUN_ACCESS_KEY")); + config.setAccessSecret(System.getenv("ALIYUN_SECRET_KEY")); + config.setBucket("yunai-aoteman"); + config.setDomain(null); // 如果有自定义域名,则可以设置。http://ali-oss.iocoder.cn + // 默认北京的 endpoint + config.setEndpoint("oss-cn-beijing.aliyuncs.com"); + + // 执行上传 + testExecuteUpload(config); + } + + @Test + @Disabled // 腾讯云 COS,如果要集成测试,可以注释本行 + public void testQCloud() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 + config.setAccessKey(System.getenv("QCLOUD_ACCESS_KEY")); + config.setAccessSecret(System.getenv("QCLOUD_SECRET_KEY")); + config.setBucket("aoteman-1255880240"); + config.setDomain(null); // 如果有自定义域名,则可以设置。http://tengxun-oss.iocoder.cn + // 默认上海的 endpoint + config.setEndpoint("cos.ap-shanghai.myqcloud.com"); + + // 执行上传 + testExecuteUpload(config); + } + + @Test + @Disabled // 七牛云存储,如果要集成测试,可以注释本行 + public void testQiniu() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 +// config.setAccessKey(System.getenv("QINIU_ACCESS_KEY")); +// config.setAccessSecret(System.getenv("QINIU_SECRET_KEY")); + config.setAccessKey("b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8"); + config.setAccessSecret("kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP"); + config.setBucket("ruoyi-vue-pro"); + config.setDomain("http://test.yudao.iocoder.cn"); // 如果有自定义域名,则可以设置。http://static.yudao.iocoder.cn + // 默认上海的 endpoint + config.setEndpoint("s3-cn-south-1.qiniucs.com"); + + // 执行上传 + testExecuteUpload(config); + } + + @Test + @Disabled // 华为云存储,如果要集成测试,可以注释本行 + public void testHuaweiCloud() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 +// config.setAccessKey(System.getenv("HUAWEI_CLOUD_ACCESS_KEY")); +// config.setAccessSecret(System.getenv("HUAWEI_CLOUD_SECRET_KEY")); + config.setBucket("yudao"); + config.setDomain(null); // 如果有自定义域名,则可以设置。 + // 默认上海的 endpoint + config.setEndpoint("obs.cn-east-3.myhuaweicloud.com"); + + // 执行上传 + testExecuteUpload(config); + } + + private void testExecuteUpload(S3FileClientConfig config) throws Exception { + // 校验配置 + ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config); + // 创建 Client + S3FileClient client = new S3FileClient(0L, config); + client.init(); + // 上传文件 + String path = IdUtil.fastSimpleUUID() + ".jpg"; + byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); + String fullPath = client.upload(content, path, "image/jpeg"); + System.out.println("访问地址:" + fullPath); + // 读取文件 + if (true) { + byte[] bytes = client.getContent(path); + System.out.println("文件内容:" + bytes.length); + } + // 删除文件 + if (false) { + client.delete(path); + } + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java new file mode 100644 index 0000000..1e00cf1 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.sftp; + +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class SftpFileClientTest { + + @Test + @Disabled + public void test() { + // 创建客户端 + SftpFileClientConfig config = new SftpFileClientConfig(); + config.setDomain("http://127.0.0.1:48080"); + config.setBasePath("/home/ftp"); + config.setHost("kanchai.club"); + config.setPort(222); + config.setUsername(""); + config.setPassword(""); + SftpFileClient client = new SftpFileClient(0L, config); + client.init(); + // 上传文件 + String path = IdUtil.fastSimpleUUID() + ".jpg"; + byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); + String fullPath = client.upload(content, path, "image/jpeg"); + System.out.println("访问地址:" + fullPath); + if (false) { + byte[] bytes = client.getContent(path); + System.out.println("文件内容:" + bytes); + } + if (false) { + client.delete(path); + } + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java index 7f43225..cedb875 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java @@ -4,12 +4,12 @@ import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.map.MapUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.file.core.client.FileClient; -import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; -import cn.iocoder.yudao.framework.file.core.client.FileClientFactory; -import cn.iocoder.yudao.framework.file.core.client.local.LocalFileClient; -import cn.iocoder.yudao.framework.file.core.client.local.LocalFileClientConfig; -import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory; +import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig; +import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java index 4cceb37..66fff78 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.infra.service.file; import cn.hutool.core.io.resource.ResourceUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.file.core.client.FileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.util.AssertUtils; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; -- libgit2 0.21.4