完成 yudao-spring-boot-starter-file 组件,支持 S3 对接云存储、local、ftp、sftp、db 等协议
parent
3d40fc81dd
commit
05d4aae65d
|
@ -11,6 +11,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(PayProperties.class)
|
||||
public class YudaoPayAutoConfiguration {
|
||||
|
||||
|
|
|
@ -27,11 +27,11 @@ public class PayClientFactoryImpl implements PayClientFactory {
|
|||
* 支付客户端 Map
|
||||
* key:渠道编号
|
||||
*/
|
||||
private final ConcurrentMap<Long, AbstractPayClient<?>> channelIdClients = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Long, AbstractPayClient<?>> clients = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public PayClient getPayClient(Long channelId) {
|
||||
AbstractPayClient<?> client = channelIdClients.get(channelId);
|
||||
AbstractPayClient<?> client = clients.get(channelId);
|
||||
if (client == null) {
|
||||
log.error("[getPayClient][渠道编号({}) 找不到客户端]", channelId);
|
||||
}
|
||||
|
@ -42,11 +42,11 @@ public class PayClientFactoryImpl implements PayClientFactory {
|
|||
@SuppressWarnings("unchecked")
|
||||
public <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode,
|
||||
Config config) {
|
||||
AbstractPayClient<Config> client = (AbstractPayClient<Config>) channelIdClients.get(channelId);
|
||||
AbstractPayClient<Config> client = (AbstractPayClient<Config>) clients.get(channelId);
|
||||
if (client == null) {
|
||||
client = this.createPayClient(channelId, channelCode, config);
|
||||
client.init();
|
||||
channelIdClients.put(client.getId(), client);
|
||||
clients.put(client.getId(), client);
|
||||
} else {
|
||||
client.refresh(config);
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ public class PayClientFactoryImpl implements PayClientFactory {
|
|||
case ALIPAY_PC: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
|
||||
}
|
||||
// 创建失败,错误日志 + 抛出异常
|
||||
log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", config);
|
||||
log.error("[createPayClient][配置({}) 找不到合适的客户端实现]", config);
|
||||
throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", config));
|
||||
}
|
||||
|
||||
|
|
|
@ -56,5 +56,4 @@ public enum PayChannelEnum {
|
|||
return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 文件配置类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
public class YudaoFileAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FileClientFactory fileClientFactory() {
|
||||
return new FileClientFactoryImpl();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl;
|
||||
package cn.iocoder.yudao.framework.file.core.client;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.file.core.client.FileClient;
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client;
|
||||
|
||||
public interface FileClientFactory {
|
||||
|
||||
/**
|
||||
* 获得文件客户端
|
||||
*
|
||||
* @param channelId 渠道编号
|
||||
* @return 文件客户端
|
||||
*/
|
||||
FileClient getFileClient(Long channelId);
|
||||
|
||||
/**
|
||||
* 创建文件客户端
|
||||
*
|
||||
* @param configId 配置编号
|
||||
* @param storage 存储器的枚举 {@link cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum}
|
||||
* @param config 文件配置
|
||||
*/
|
||||
<Config extends FileClientConfig> void createOrUpdateFileClient(Long configId, Integer storage, Config config);
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
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<Long, AbstractFileClient<?>> clients = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public FileClient getFileClient(Long channelId) {
|
||||
AbstractFileClient<?> client = clients.get(channelId);
|
||||
if (client == null) {
|
||||
log.error("[getFileClient][配置编号({}) 找不到客户端]", channelId);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <Config extends FileClientConfig> void createOrUpdateFileClient(Long configId, Integer storage, Config config) {
|
||||
AbstractFileClient<Config> client = (AbstractFileClient<Config>) 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 <Config extends FileClientConfig> AbstractFileClient<Config> createFileClient(
|
||||
Long configId, Integer storage, Config config) {
|
||||
FileStorageEnum storageEnum = FileStorageEnum.getByStorage(storage);
|
||||
Assert.notNull(storageEnum, String.format("文件配置(%s) 为空", storageEnum));
|
||||
// 创建客户端
|
||||
// switch (storageEnum) {
|
||||
// case WX_PUB: return (AbstractFileClient<Config>) new WXPubFileClient(channelId, (WXFileClientConfig) config);
|
||||
// case WX_LITE: return (AbstractFileClient<Config>) new WXPubFileClient(channelId, (WXFileClientConfig) config);
|
||||
// case WX_APP: return (AbstractFileClient<Config>) new WXPubFileClient(channelId, (WXFileClientConfig) config);
|
||||
// case ALIPAY_WAP: return (AbstractFileClient<Config>) new AlipayWapFileClient(channelId, (AlipayFileClientConfig) config);
|
||||
// case ALIPAY_QR: return (AbstractFileClient<Config>) new AlipayQrFileClient(channelId, (AlipayFileClientConfig) config);
|
||||
// case ALIPAY_APP: return (AbstractFileClient<Config>) new AlipayQrFileClient(channelId, (AlipayFileClientConfig) config);
|
||||
// case ALIPAY_PC: return (AbstractFileClient<Config>) new AlipayQrFileClient(channelId, (AlipayFileClientConfig) config);
|
||||
// }
|
||||
return (AbstractFileClient<Config>) ReflectUtil.newInstance(storageEnum.getClientClass(), configId, config);
|
||||
// storageEnum.getClientClass().newInstance()
|
||||
// // 创建失败,错误日志 + 抛出异常
|
||||
// log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", config);
|
||||
// throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", config));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
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() {
|
||||
dao = SpringUtil.getBean(DBFileContentFrameworkDAO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String upload(byte[] content, String path) {
|
||||
dao.insert(getId(), path, content);
|
||||
// 拼接返回路径
|
||||
return super.formatFileUrl(config.getDomain(), path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String path) {
|
||||
dao.delete(getId(), path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getContent(String path) {
|
||||
return dao.selectContent(getId(), path);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
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 javax.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* 基于 DB 存储的文件客户端的配置类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class DBFileClientConfig implements FileClientConfig {
|
||||
|
||||
/**
|
||||
* 自定义域名
|
||||
*/
|
||||
@NotEmpty(message = "domain 不能为空")
|
||||
@URL(message = "domain 必须是 URL 格式")
|
||||
private String domain;
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.db;
|
||||
|
||||
/**
|
||||
* 文件内容 Framework DAO 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface DBFileContentFrameworkDAO {
|
||||
|
||||
void insert(Long configId, String path, byte[] content);
|
||||
|
||||
void delete(Long configId, String path);
|
||||
|
||||
byte[] selectContent(Long configId, String path);
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl.ftp;
|
||||
package cn.iocoder.yudao.framework.file.core.client.ftp;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
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.FtpException;
|
||||
import cn.hutool.extra.ftp.FtpMode;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.AbstractFileClient;
|
||||
import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl.ftp;
|
||||
package cn.iocoder.yudao.framework.file.core.client.ftp;
|
||||
|
||||
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig;
|
||||
import lombok.Data;
|
|
@ -1,7 +1,7 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl.local;
|
||||
package cn.iocoder.yudao.framework.file.core.client.local;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.AbstractFileClient;
|
||||
import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl.local;
|
||||
package cn.iocoder.yudao.framework.file.core.client.local;
|
||||
|
||||
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig;
|
||||
import lombok.Data;
|
|
@ -1,7 +1,7 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl.s3;
|
||||
package cn.iocoder.yudao.framework.file.core.client.s3;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.AbstractFileClient;
|
||||
import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.core.sync.RequestBody;
|
||||
|
@ -13,7 +13,7 @@ import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
|||
|
||||
import java.net.URI;
|
||||
|
||||
import static cn.iocoder.yudao.framework.file.core.client.impl.s3.S3FileClientConfig.ENDPOINT_QINIU;
|
||||
import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_QINIU;
|
||||
|
||||
/**
|
||||
* 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl.s3;
|
||||
package cn.iocoder.yudao.framework.file.core.client.s3;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig;
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl.s3;
|
||||
package cn.iocoder.yudao.framework.file.core.client.s3;
|
||||
|
||||
import software.amazon.awssdk.core.interceptor.Context;
|
||||
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
|
|
@ -1,9 +1,9 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl.sftp;
|
||||
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.impl.AbstractFileClient;
|
||||
import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.yudao.framework.file.core.client.impl.sftp;
|
||||
package cn.iocoder.yudao.framework.file.core.client.sftp;
|
||||
|
||||
import cn.iocoder.yudao.framework.file.core.client.FileClientConfig;
|
||||
import lombok.Data;
|
|
@ -0,0 +1,55 @@
|
|||
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());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.iocoder.yudao.framework.file.config.YudaoFileAutoConfiguration
|
|
@ -3,8 +3,6 @@ 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 cn.iocoder.yudao.framework.file.core.client.impl.ftp.FtpFileClient;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.ftp.FtpFileClientConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class FtpFileClientTest {
|
||||
|
|
|
@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.file.core.client.local;
|
|||
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.local.LocalFileClient;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.local.LocalFileClientConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class LocalFileClientTest {
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* 占位,避免 package 无法提交到 Git 仓库
|
||||
*/
|
||||
package cn.iocoder.yudao.framework.file.core.client;
|
|
@ -4,8 +4,6 @@ import cn.hutool.core.io.resource.ResourceUtil;
|
|||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.s3.S3FileClient;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.s3.S3FileClientConfig;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.file.core.client.sftp;
|
|||
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.sftp.SftpFileClient;
|
||||
import cn.iocoder.yudao.framework.file.core.client.impl.sftp.SftpFileClientConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SftpFileClientTest {
|
||||
|
|
Loading…
Reference in New Issue