全局:简化 file 组件,融合到 infra 模块

This commit is contained in:
YunaiV 2024-02-28 18:59:33 +08:00
parent 067ff6cc4d
commit 77d634038c
48 changed files with 300 additions and 367 deletions

View File

@ -566,11 +566,6 @@
<artifactId>okhttp</artifactId> <artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version> <version>${okhttp3.version}</version>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-file</artifactId>
<version>${revision}</version>
</dependency>
<dependency> <dependency>
<groupId>io.minio</groupId> <groupId>io.minio</groupId>
<artifactId>minio</artifactId> <artifactId>minio</artifactId>

View File

@ -16,7 +16,6 @@
<module>yudao-spring-boot-starter-web</module> <module>yudao-spring-boot-starter-web</module>
<module>yudao-spring-boot-starter-security</module> <module>yudao-spring-boot-starter-security</module>
<module>yudao-spring-boot-starter-file</module>
<module>yudao-spring-boot-starter-monitor</module> <module>yudao-spring-boot-starter-monitor</module>
<module>yudao-spring-boot-starter-protection</module> <module>yudao-spring-boot-starter-protection</module>
<module>yudao-spring-boot-starter-job</module> <module>yudao-spring-boot-starter-job</module>

View File

@ -136,6 +136,21 @@ public class JsonUtils {
} }
} }
/**
* 解析 JSON 字符串成指定类型的对象如果解析失败则返回 null
*
* @param text 字符串
* @param typeReference 类型引用
* @return 指定类型的对象
*/
public static <T> T parseObjectQuietly(String text, TypeReference<T> typeReference) {
try {
return objectMapper.readValue(text, typeReference);
} catch (IOException e) {
return null;
}
}
public static <T> List<T> parseArray(String text, Class<T> clazz) { public static <T> List<T> parseArray(String text, Class<T> clazz) {
if (StrUtil.isEmpty(text)) { if (StrUtil.isEmpty(text)) {
return new ArrayList<>(); return new ArrayList<>();

View File

@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yudao-framework</artifactId>
<groupId>cn.iocoder.boot</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-spring-boot-starter-file</artifactId>
<name>${project.artifactId}</name>
<description>文件客户端,支持多种存储器
1. file本地磁盘
2. ftpFTP 服务器
2. sftpSFTP 服务器
4. db数据库
5. s3支持 S3 协议的云存储服务,例如说 MinIO、阿里云、华为云、腾讯云、七牛云等等
</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-common</artifactId>
</dependency>
<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 -->
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId> <!-- 解决 sftp 连接 -->
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <!-- 文件类型的识别 -->
</dependency>
<!-- 三方云服务相关 -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -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();
}
}

View File

@ -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<DBFileClientConfig> {
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;
}
}

View File

@ -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);
}

View File

@ -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<? extends FileClientConfig> configClass;
/**
* 客户端类
*/
private final Class<? extends FileClient> clientClass;
public static FileStorageEnum getByStorage(Integer storage) {
return ArrayUtil.firstMatch(o -> o.getStorage().equals(storage), values());
}
}

View File

@ -1,4 +0,0 @@
/**
* 占位避免 package 无法提交到 Git 仓库
*/
package cn.iocoder.yudao.framework.file.config;

View File

@ -1,4 +0,0 @@
/**
* 占位避免 package 无法提交到 Git 仓库
*/
package cn.iocoder.yudao.framework.file.core.enums;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -117,8 +117,21 @@
<!-- 三方云服务相关 --> <!-- 三方云服务相关 -->
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>commons-net</groupId>
<artifactId>yudao-spring-boot-starter-file</artifactId> <artifactId>commons-net</artifactId> <!-- 文件客户端:解决 ftp 连接 -->
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 -->
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId> <!-- 文件客户端解决阿里云、腾讯云、minio 等 S3 连接 -->
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <!-- 文件客户端:文件类型的识别 -->
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.infra.controller.admin.file.vo.config; 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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;

View File

@ -1,12 +1,20 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.file; package cn.iocoder.yudao.module.infra.dal.dataobject.file;
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 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.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; 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.*; import lombok.*;
/** /**
@ -52,7 +60,42 @@ public class FileConfigDO extends BaseDO {
/** /**
* 支付渠道配置 * 支付渠道配置
*/ */
@TableField(typeHandler = JacksonTypeHandler.class) @TableField(typeHandler = FileClientConfigTypeHandler.class)
private FileClientConfig config; private FileClientConfig config;
public static class FileClientConfigTypeHandler extends AbstractJsonTypeHandler<Object> {
@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);
}
}
} }

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.file; package cn.iocoder.yudao.module.infra.dal.dataobject.file;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 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.IdType;
import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId; 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 芋道源码 * @author 芋道源码
*/ */

View File

@ -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<FileContentDO> 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<FileContentDO> buildQuery(Long configId, String path) {
return new LambdaQueryWrapper<FileContentDO>()
.eq(FileContentDO::getConfigId, configId)
.eq(FileContentDO::getPath, path);
}
}

View File

@ -1,9 +1,25 @@
package cn.iocoder.yudao.module.infra.dal.mysql.file; package cn.iocoder.yudao.module.infra.dal.mysql.file;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileContentDO; 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 com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper @Mapper
public interface FileContentMapper extends BaseMapper<FileContentDO> { public interface FileContentMapper extends BaseMapper<FileContentDO> {
default void deleteByConfigIdAndPath(Long configId, String path) {
this.delete(new LambdaQueryWrapper<FileContentDO>()
.eq(FileContentDO::getConfigId, configId)
.eq(FileContentDO::getPath, path));
}
default List<FileContentDO> selectListByConfigIdAndPath(Long configId, String path) {
return selectList(new LambdaQueryWrapper<FileContentDO>()
.eq(FileContentDO::getConfigId, configId)
.eq(FileContentDO::getPath, path));
}
} }

View File

@ -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();
}
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.framework.file.core.client; package cn.iocoder.yudao.module.infra.framework.file.core.client;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.file.core.client; package cn.iocoder.yudao.module.infra.framework.file.core.client;
import cn.iocoder.yudao.framework.file.core.client.s3.FilePresignedUrlRespDTO; import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
/** /**
* 文件客户端 * 文件客户端

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.framework.file.core.client; package cn.iocoder.yudao.module.infra.framework.file.core.client;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;

View File

@ -1,4 +1,6 @@
package cn.iocoder.yudao.framework.file.core.client; package cn.iocoder.yudao.module.infra.framework.file.core.client;
import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;
public interface FileClientFactory { public interface FileClientFactory {
@ -14,7 +16,7 @@ public interface FileClientFactory {
* 创建文件客户端 * 创建文件客户端
* *
* @param configId 配置编号 * @param configId 配置编号
* @param storage 存储器的枚举 {@link cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum} * @param storage 存储器的枚举 {@link FileStorageEnum}
* @param config 文件配置 * @param config 文件配置
*/ */
<Config extends FileClientConfig> void createOrUpdateFileClient(Long configId, Integer storage, Config config); <Config extends FileClientConfig> void createOrUpdateFileClient(Long configId, Integer storage, Config config);

View File

@ -1,8 +1,8 @@
package cn.iocoder.yudao.framework.file.core.client; package cn.iocoder.yudao.module.infra.framework.file.core.client;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum; import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;

View File

@ -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<DBFileClientConfig> {
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<FileContentDO> list = fileContentMapper.selectListByConfigIdAndPath(getId(), path);
if (CollUtil.isEmpty(list)) {
return null;
}
// 排序后 id 最大的即最后上传的
list.sort(Comparator.comparing(FileContentDO::getId));
return CollUtil.getLast(list).getContent();
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.file.core.client.db; package cn.iocoder.yudao.module.infra.framework.file.core.client.db;
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.framework.file.core.client.ftp; package cn.iocoder.yudao.module.infra.framework.file.core.client.ftp;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.ftp.Ftp; import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.ftp.FtpException; import cn.hutool.extra.ftp.FtpException;
import cn.hutool.extra.ftp.FtpMode; import cn.hutool.extra.ftp.FtpMode;
import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.file.core.client.ftp; package cn.iocoder.yudao.module.infra.framework.file.core.client.ftp;
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.file.core.client.local; package cn.iocoder.yudao.module.infra.framework.file.core.client.local;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
import java.io.File; import java.io.File;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.file.core.client.local; package cn.iocoder.yudao.module.infra.framework.file.core.client.local;
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.framework.file.core.client.s3; package cn.iocoder.yudao.module.infra.framework.file.core.client.s3;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View File

@ -1,18 +1,15 @@
package cn.iocoder.yudao.framework.file.core.client.s3; package cn.iocoder.yudao.module.infra.framework.file.core.client.s3;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
import io.minio.*; import io.minio.*;
import io.minio.http.Method; import io.minio.http.Method;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.concurrent.TimeUnit; 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 协议的文件客户端实现 MinIO阿里云腾讯云七牛云华为云等云服务
* <p> * <p>
@ -76,15 +73,15 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
*/ */
private String buildRegion() { private String buildRegion() {
// 阿里云必须有 region否则会报错 // 阿里云必须有 region否则会报错
if (config.getEndpoint().contains(ENDPOINT_ALIYUN)) { if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_ALIYUN)) {
return StrUtil.subBefore(config.getEndpoint(), '.', false) return StrUtil.subBefore(config.getEndpoint(), '.', false)
.replaceAll("-internal", "")// 去除内网 Endpoint 的后缀 .replaceAll("-internal", "")// 去除内网 Endpoint 的后缀
.replaceAll("https://", ""); .replaceAll("https://", "");
} }
// 腾讯云必须有 region否则会报错 // 腾讯云必须有 region否则会报错
if (config.getEndpoint().contains(ENDPOINT_TENCENT)) { if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_TENCENT)) {
return StrUtil.subAfter(config.getEndpoint(), "cos.", false) return StrUtil.subAfter(config.getEndpoint(), "cos.", false)
.replaceAll("." + ENDPOINT_TENCENT, ""); // 去除 Endpoint .replaceAll("." + S3FileClientConfig.ENDPOINT_TENCENT, ""); // 去除 Endpoint
} }
return null; return null;
} }

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.file.core.client.s3; package cn.iocoder.yudao.module.infra.framework.file.core.client.s3;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;

View File

@ -1,9 +1,9 @@
package cn.iocoder.yudao.framework.file.core.client.sftp; package cn.iocoder.yudao.module.infra.framework.file.core.client.sftp;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.ssh.Sftp; import cn.hutool.extra.ssh.Sftp;
import cn.iocoder.yudao.framework.common.util.io.FileUtils; import cn.iocoder.yudao.framework.common.util.io.FileUtils;
import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
import java.io.File; import java.io.File;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.file.core.client.sftp; package cn.iocoder.yudao.module.infra.framework.file.core.client.sftp;
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;

View File

@ -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<? extends FileClientConfig> configClass;
/**
* 客户端类
*/
private final Class<? extends FileClient> clientClass;
public static FileStorageEnum getByStorage(Integer storage) {
return ArrayUtil.firstMatch(o -> o.getStorage().equals(storage), values());
}
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.framework.file.core.utils; package cn.iocoder.yudao.module.infra.framework.file.core.utils;
import com.alibaba.ttl.TransmittableThreadLocal; import com.alibaba.ttl.TransmittableThreadLocal;
import lombok.SneakyThrows; import lombok.SneakyThrows;

View File

@ -0,0 +1,12 @@
/**
* 文件客户端支持多种存储器
*
* 1. local本地磁盘
* 2. ftpFTP 服务器
* 3. sftpSFTP 服务器
* 4. db数据库
* 5. s3支持 S3 协议的云存储服务例如说 MinIO阿里云华为云腾讯云七牛云等等
*
* @author 芋道源码
*/
package cn.iocoder.yudao.module.infra.framework.file;

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.infra.service.file; package cn.iocoder.yudao.module.infra.service.file;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.FileConfigPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;

View File

@ -5,10 +5,10 @@ import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
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.file.core.client.FileClientConfig; import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;
import cn.iocoder.yudao.framework.file.core.client.FileClientFactory; import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory;
import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum; 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.FileConfigPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;
import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert; import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert;

View File

@ -5,9 +5,9 @@ import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.io.FileUtils; import cn.iocoder.yudao.framework.common.util.io.FileUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
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.file.core.client.s3.FilePresignedUrlRespDTO; import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
import cn.iocoder.yudao.framework.file.core.utils.FileTypeUtils; 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.FileCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 0 B

View File

@ -1,8 +1,10 @@
package cn.iocoder.yudao.framework.file.core.client.ftp; package cn.iocoder.yudao.module.infra.framework.file.core.ftp;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.ftp.FtpMode; 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.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.framework.file.core.client.local; package cn.iocoder.yudao.module.infra.framework.file.core.local;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.IdUtil; 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.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View File

@ -1,13 +1,14 @@
package cn.iocoder.yudao.framework.file.core.client.s3; package cn.iocoder.yudao.module.infra.framework.file.core.s3;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; 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.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import jakarta.validation.Validation;
public class S3FileClientTest { public class S3FileClientTest {
@Test @Test

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.framework.file.core.client.sftp; package cn.iocoder.yudao.module.infra.framework.file.core.sftp;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.IdUtil; 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.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View File

@ -4,12 +4,12 @@ import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.framework.file.core.client.FileClientConfig; import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;
import cn.iocoder.yudao.framework.file.core.client.FileClientFactory; import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory;
import cn.iocoder.yudao.framework.file.core.client.local.LocalFileClient; import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient;
import cn.iocoder.yudao.framework.file.core.client.local.LocalFileClientConfig; import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig;
import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum; import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 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.FileConfigPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;

View File

@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.infra.service.file;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 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.ut.BaseDbUnitTest;
import cn.iocoder.yudao.framework.test.core.util.AssertUtils; import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;