最新公众号管理平台后端
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4" />
|
||||
84
joju-framework/joju-spring-boot-starter-file/pom.xml
Normal file
84
joju-framework/joju-spring-boot-starter-file/pom.xml
Normal file
@@ -0,0 +1,84 @@
|
||||
<?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>
|
||||
<groupId>com.jojubanking.boot</groupId>
|
||||
<artifactId>joju-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>joju-spring-boot-starter-file</artifactId>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>文件客户端,支持多种存储器
|
||||
1. file:本地磁盘
|
||||
2. ftp:FTP 服务器
|
||||
2. sftp:SFTP 服务器
|
||||
4. db:数据库
|
||||
5. s3:支持 S3 协议的云存储服务,例如说 MinIO、阿里云、华为云、腾讯云、七牛云等等
|
||||
</description>
|
||||
<url>https://www.jojubanking.com</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jojubanking.boot</groupId>
|
||||
<artifactId>joju-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>com.jojubanking.boot</groupId>
|
||||
<artifactId>joju-spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.jojubanking.boot.framework.file.config;
|
||||
|
||||
import com.jojubanking.boot.framework.file.core.client.FileClientFactory;
|
||||
import com.jojubanking.boot.framework.file.core.client.FileClientFactoryImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 文件配置类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Configuration
|
||||
public class JojuFileAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FileClientFactory fileClientFactory() {
|
||||
return new FileClientFactoryImpl();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.jojubanking.boot.framework.file.core.client;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 文件客户端的抽象类,提供模板方法,减少子类的冗余代码
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractFileClient<Config extends FileClientConfig> 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.info("[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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.jojubanking.boot.framework.file.core.client;
|
||||
|
||||
/**
|
||||
* 文件客户端
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public interface FileClient {
|
||||
|
||||
/**
|
||||
* 获得客户端编号
|
||||
*
|
||||
* @return 客户端编号
|
||||
*/
|
||||
Long getId();
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param content 文件流
|
||||
* @param path 相对路径
|
||||
* @return 完整路径,即 HTTP 访问地址
|
||||
* @throws Exception 上传文件时,抛出 Exception 异常
|
||||
*/
|
||||
String upload(byte[] content, String path) throws Exception;
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param path 相对路径
|
||||
* @throws Exception 删除文件时,抛出 Exception 异常
|
||||
*/
|
||||
void delete(String path) throws Exception;
|
||||
|
||||
/**
|
||||
* 获得文件的内容
|
||||
*
|
||||
* @param path 相对路径
|
||||
* @return 文件的内容
|
||||
*/
|
||||
byte[] getContent(String path) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.jojubanking.boot.framework.file.core.client;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* 文件客户端的配置
|
||||
* 不同实现的客户端,需要不同的配置,通过子类来定义
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
// @JsonTypeInfo 注解的作用,Jackson 多态
|
||||
// 1. 序列化到时数据库时,增加 @class 属性。
|
||||
// 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型
|
||||
public interface FileClientConfig {
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.jojubanking.boot.framework.file.core.client;
|
||||
|
||||
import com.jojubanking.boot.framework.file.core.enums.FileStorageEnum;
|
||||
|
||||
public interface FileClientFactory {
|
||||
|
||||
/**
|
||||
* 获得文件客户端
|
||||
*
|
||||
* @param configId 配置编号
|
||||
* @return 文件客户端
|
||||
*/
|
||||
FileClient getFileClient(Long configId);
|
||||
|
||||
/**
|
||||
* 创建文件客户端
|
||||
*
|
||||
* @param configId 配置编号
|
||||
* @param storage 存储器的枚举 {@link FileStorageEnum}
|
||||
* @param config 文件配置
|
||||
*/
|
||||
<Config extends FileClientConfig> void createOrUpdateFileClient(Long configId, Integer storage, Config config);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.jojubanking.boot.framework.file.core.client;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.jojubanking.boot.framework.file.core.enums.FileStorageEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 文件客户端的工厂实现类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Slf4j
|
||||
public class FileClientFactoryImpl implements FileClientFactory {
|
||||
|
||||
/**
|
||||
* 文件客户端 Map
|
||||
* key:配置编号
|
||||
*/
|
||||
private final ConcurrentMap<Long, AbstractFileClient<?>> 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 <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));
|
||||
// 创建客户端
|
||||
return (AbstractFileClient<Config>) ReflectUtil.newInstance(storageEnum.getClientClass(), configId, config);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.db;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.jojubanking.boot.framework.file.core.client.AbstractFileClient;
|
||||
|
||||
/**
|
||||
* 基于 DB 存储的文件客户端的配置类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.db;
|
||||
|
||||
import com.jojubanking.boot.framework.file.core.client.FileClientConfig;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* 基于 DB 存储的文件客户端的配置类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Data
|
||||
public class DBFileClientConfig implements FileClientConfig {
|
||||
|
||||
/**
|
||||
* 自定义域名
|
||||
*/
|
||||
@NotEmpty(message = "domain 不能为空")
|
||||
@URL(message = "domain 必须是 URL 格式")
|
||||
private String domain;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.db;
|
||||
|
||||
/**
|
||||
* 文件内容 Framework DAO 接口
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.jojubanking.boot.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 com.jojubanking.boot.framework.file.core.client.AbstractFileClient;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* Ftp 文件客户端
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> {
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.ftp;
|
||||
|
||||
import com.jojubanking.boot.framework.file.core.client.FileClientConfig;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* Ftp 文件客户端的配置类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@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;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.local;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import com.jojubanking.boot.framework.file.core.client.AbstractFileClient;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 本地文件客户端
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public class LocalFileClient extends AbstractFileClient<LocalFileClientConfig> {
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.local;
|
||||
|
||||
import com.jojubanking.boot.framework.file.core.client.FileClientConfig;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* 本地文件客户端的配置类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class LocalFileClientConfig implements FileClientConfig {
|
||||
|
||||
/**
|
||||
* 基础路径
|
||||
*/
|
||||
@NotEmpty(message = "基础路径不能为空")
|
||||
private String basePath;
|
||||
|
||||
/**
|
||||
* 自定义域名
|
||||
*/
|
||||
@NotEmpty(message = "domain 不能为空")
|
||||
@URL(message = "domain 必须是 URL 格式")
|
||||
private String domain;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.s3;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.jojubanking.boot.framework.file.core.client.AbstractFileClient;
|
||||
import io.minio.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import static com.jojubanking.boot.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_ALIYUN;
|
||||
|
||||
/**
|
||||
* 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
|
||||
*
|
||||
* S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
|
||||
|
||||
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 的后缀
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String upload(byte[] content, String path) throws Exception {
|
||||
// 执行上传
|
||||
client.putObject(PutObjectArgs.builder()
|
||||
.bucket(config.getBucket()) // bucket 必须传递
|
||||
.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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.s3;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.jojubanking.boot.framework.file.core.client.FileClientConfig;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* S3 文件客户端的配置类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Data
|
||||
public class S3FileClientConfig implements FileClientConfig {
|
||||
|
||||
public static final String ENDPOINT_QINIU = "qiniucs.com";
|
||||
public static final String ENDPOINT_ALIYUN = "aliyuncs.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.sftp;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.extra.ssh.Sftp;
|
||||
import com.jojubanking.boot.framework.common.util.io.FileUtils;
|
||||
import com.jojubanking.boot.framework.file.core.client.AbstractFileClient;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Sftp 文件客户端
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public class SftpFileClient extends AbstractFileClient<SftpFileClientConfig> {
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.sftp;
|
||||
|
||||
import com.jojubanking.boot.framework.file.core.client.FileClientConfig;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* Sftp 文件客户端的配置类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@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;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.jojubanking.boot.framework.file.core.enums;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.jojubanking.boot.framework.file.core.client.FileClient;
|
||||
import com.jojubanking.boot.framework.file.core.client.FileClientConfig;
|
||||
import com.jojubanking.boot.framework.file.core.client.db.DBFileClient;
|
||||
import com.jojubanking.boot.framework.file.core.client.db.DBFileClientConfig;
|
||||
import com.jojubanking.boot.framework.file.core.client.ftp.FtpFileClient;
|
||||
import com.jojubanking.boot.framework.file.core.client.ftp.FtpFileClientConfig;
|
||||
import com.jojubanking.boot.framework.file.core.client.local.LocalFileClient;
|
||||
import com.jojubanking.boot.framework.file.core.client.local.LocalFileClientConfig;
|
||||
import com.jojubanking.boot.framework.file.core.client.s3.S3FileClient;
|
||||
import com.jojubanking.boot.framework.file.core.client.s3.S3FileClientConfig;
|
||||
import com.jojubanking.boot.framework.file.core.client.sftp.SftpFileClient;
|
||||
import com.jojubanking.boot.framework.file.core.client.sftp.SftpFileClientConfig;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 文件存储器枚举
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@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,48 @@
|
||||
package com.jojubanking.boot.framework.file.core.utils;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.tika.Tika;
|
||||
|
||||
/**
|
||||
* 文件类型 Utils
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public class FileTypeUtils {
|
||||
|
||||
private static final ThreadLocal<Tika> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.jojubanking.boot.framework.file.config.JojuFileAutoConfiguration
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位,避免 package 无法提交到 Git 仓库
|
||||
*/
|
||||
package com.jojubanking.boot.framework.file.config;
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.jojubanking.boot.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);
|
||||
System.out.println("访问地址:" + fullPath);
|
||||
if (false) {
|
||||
byte[] bytes = client.getContent(path);
|
||||
System.out.println("文件内容:" + bytes);
|
||||
}
|
||||
if (false) {
|
||||
client.delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.jojubanking.boot.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);
|
||||
System.out.println("访问地址:" + fullPath);
|
||||
client.delete(path);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.jojubanking.boot.framework.file.core.client.s3;
|
||||
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.jojubanking.boot.framework.common.util.validation.ValidationUtils;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.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("jojuyuanma");
|
||||
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.joju.iocoder.cn"); // 如果有自定义域名,则可以设置。http://static.joju.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("joju");
|
||||
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);
|
||||
System.out.println("访问地址:" + fullPath);
|
||||
// 读取文件
|
||||
if (true) {
|
||||
byte[] bytes = client.getContent(path);
|
||||
System.out.println("文件内容:" + bytes.length);
|
||||
}
|
||||
// 删除文件
|
||||
if (false) {
|
||||
client.delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.jojubanking.boot.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);
|
||||
System.out.println("访问地址:" + fullPath);
|
||||
if (false) {
|
||||
byte[] bytes = client.getContent(path);
|
||||
System.out.println("文件内容:" + bytes);
|
||||
}
|
||||
if (false) {
|
||||
client.delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位,避免 package 无法提交到 Git 仓库
|
||||
*/
|
||||
package com.jojubanking.boot.framework.file.core.enums;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.jojubanking.boot.framework.file.config.JojuFileAutoConfiguration
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
#Generated by Maven
|
||||
#Wed Jan 14 17:49:09 CST 2026
|
||||
version=2.0.0-beta
|
||||
groupId=com.jojubanking.boot
|
||||
artifactId=joju-spring-boot-starter-file
|
||||
@@ -0,0 +1,19 @@
|
||||
com\jojubanking\boot\framework\file\core\client\s3\S3FileClient.class
|
||||
com\jojubanking\boot\framework\file\core\client\sftp\SftpFileClient.class
|
||||
com\jojubanking\boot\framework\file\core\client\ftp\FtpFileClientConfig.class
|
||||
com\jojubanking\boot\framework\file\core\client\db\DBFileClientConfig.class
|
||||
com\jojubanking\boot\framework\file\core\client\ftp\FtpFileClient.class
|
||||
com\jojubanking\boot\framework\file\core\client\sftp\SftpFileClientConfig.class
|
||||
com\jojubanking\boot\framework\file\core\client\db\DBFileClient.class
|
||||
com\jojubanking\boot\framework\file\core\client\local\LocalFileClientConfig.class
|
||||
com\jojubanking\boot\framework\file\core\client\s3\S3FileClientConfig.class
|
||||
com\jojubanking\boot\framework\file\core\utils\FileTypeUtils.class
|
||||
com\jojubanking\boot\framework\file\core\client\FileClientFactoryImpl.class
|
||||
com\jojubanking\boot\framework\file\core\client\local\LocalFileClient.class
|
||||
com\jojubanking\boot\framework\file\core\client\FileClientFactory.class
|
||||
com\jojubanking\boot\framework\file\core\client\FileClientConfig.class
|
||||
com\jojubanking\boot\framework\file\config\JojuFileAutoConfiguration.class
|
||||
com\jojubanking\boot\framework\file\core\client\AbstractFileClient.class
|
||||
com\jojubanking\boot\framework\file\core\client\FileClient.class
|
||||
com\jojubanking\boot\framework\file\core\client\db\DBFileContentFrameworkDAO.class
|
||||
com\jojubanking\boot\framework\file\core\enums\FileStorageEnum.class
|
||||
@@ -0,0 +1,19 @@
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\FileClientFactory.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\enums\FileStorageEnum.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\local\LocalFileClientConfig.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\local\LocalFileClient.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\s3\S3FileClientConfig.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\sftp\SftpFileClientConfig.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\ftp\FtpFileClient.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\ftp\FtpFileClientConfig.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\utils\FileTypeUtils.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\FileClientFactoryImpl.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\config\JojuFileAutoConfiguration.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\AbstractFileClient.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\sftp\SftpFileClient.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\db\DBFileClient.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\s3\S3FileClient.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\FileClientConfig.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\FileClient.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\db\DBFileClientConfig.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\main\java\com\jojubanking\boot\framework\file\core\client\db\DBFileContentFrameworkDAO.java
|
||||
@@ -0,0 +1,4 @@
|
||||
com\jojubanking\boot\framework\file\core\client\ftp\FtpFileClientTest.class
|
||||
com\jojubanking\boot\framework\file\core\client\local\LocalFileClientTest.class
|
||||
com\jojubanking\boot\framework\file\core\client\sftp\SftpFileClientTest.class
|
||||
com\jojubanking\boot\framework\file\core\client\s3\S3FileClientTest.class
|
||||
@@ -0,0 +1,6 @@
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\test\java\com\jojubanking\boot\framework\file\core\client\local\LocalFileClientTest.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\test\java\com\jojubanking\boot\framework\file\config\package-info.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\test\java\com\jojubanking\boot\framework\file\core\client\s3\S3FileClientTest.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\test\java\com\jojubanking\boot\framework\file\core\enums\package-info.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\test\java\com\jojubanking\boot\framework\file\core\client\sftp\SftpFileClientTest.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-file\src\test\java\com\jojubanking\boot\framework\file\core\client\ftp\FtpFileClientTest.java
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Reference in New Issue
Block a user