diff --git a/orion-ops-dependencies/pom.xml b/orion-ops-dependencies/pom.xml index fad1064d..80ddfb4f 100644 --- a/orion-ops-dependencies/pom.xml +++ b/orion-ops-dependencies/pom.xml @@ -107,6 +107,11 @@ orion-ops-spring-boot-starter-log ${revision} + + com.orion.ops + orion-ops-spring-boot-starter-storage + ${revision} + diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/pom.xml b/orion-ops-framework/orion-ops-spring-boot-starter-storage/pom.xml new file mode 100644 index 00000000..02c192d1 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/pom.xml @@ -0,0 +1,31 @@ + + + + com.orion.ops + orion-ops-framework + ${revision} + + + 4.0.0 + orion-ops-spring-boot-starter-storage + ${project.artifactId} + jar + + 项目存储层配置包 + https://github.com/lijiahangmax/orion-ops-pro + + + + com.orion.ops + orion-ops-common + + + + org.springframework.boot + spring-boot-starter + + + + \ No newline at end of file diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/config/OrionStorageAutoConfiguration.java b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/config/OrionStorageAutoConfiguration.java new file mode 100644 index 00000000..32a02c09 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/config/OrionStorageAutoConfiguration.java @@ -0,0 +1,32 @@ +package com.orion.ops.framework.storage.config; + +import com.orion.ops.framework.storage.core.client.FileClient; +import com.orion.ops.framework.storage.core.client.local.LocalFileClient; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +/** + * 存储配置类 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/30 16:21 + *

+ * TODO 后续添加 FAST MINIO OSS 等 + */ +@AutoConfiguration +@EnableConfigurationProperties(StorageConfig.class) +public class OrionStorageAutoConfiguration { + + /** + * 本地文件客户端 + */ + @Bean + @Primary + public FileClient localFileClient(StorageConfig config) { + return new LocalFileClient(config.getLocal()); + } + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/config/StorageConfig.java b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/config/StorageConfig.java new file mode 100644 index 00000000..b3c52bb1 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/config/StorageConfig.java @@ -0,0 +1,23 @@ +package com.orion.ops.framework.storage.config; + +import com.orion.ops.framework.storage.core.client.local.LocalFileClientConfig; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 存储配置 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/30 18:40 + */ +@Data +@ConfigurationProperties(prefix = "orion.storage") +public class StorageConfig { + + /** + * 本地文件客户端 配置 + */ + private LocalFileClientConfig local; + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/AbstractFileClient.java b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/AbstractFileClient.java new file mode 100644 index 00000000..30ab0c93 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/AbstractFileClient.java @@ -0,0 +1,126 @@ +package com.orion.ops.framework.storage.core.client; + +import com.orion.lang.id.UUIds; +import com.orion.lang.utils.io.Files1; +import com.orion.lang.utils.io.Streams; +import com.orion.ops.framework.common.meta.TraceIdHolder; + +import java.io.InputStream; + +/** + * 文件客户端 基类 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/30 17:24 + */ +public abstract class AbstractFileClient implements FileClient { + + protected Config config; + + public AbstractFileClient(Config config) { + this.config = config; + } + + @Override + public String upload(String path, byte[] content) throws Exception { + return this.doUpload(path, Streams.toInputStream(content), true, true); + } + + @Override + public String upload(String path, byte[] content, boolean overrideIfExist) throws Exception { + return this.doUpload(path, Streams.toInputStream(content), true, overrideIfExist); + } + + @Override + public String upload(String path, InputStream in) throws Exception { + return this.doUpload(path, in, false, true); + } + + @Override + public String upload(String path, InputStream in, boolean autoClose) throws Exception { + return this.doUpload(path, in, autoClose, true); + } + + @Override + public String upload(String path, InputStream in, boolean autoClose, boolean overrideIfExist) throws Exception { + return this.doUpload(path, in, autoClose, overrideIfExist); + } + + @Override + public byte[] getContent(String path) throws Exception { + try (InputStream in = this.doDownload(path)) { + return Streams.toByteArray(in); + } + } + + @Override + public InputStream getContentInputStream(String path) throws Exception { + return this.doDownload(path); + } + + /** + * 执行上传操作 + * + * @param path path + * @param in in + * @param autoClose autoClose + * @param overrideIfExist 文件存在是否覆盖 + * @return path + * @throws Exception Exception + */ + protected abstract String doUpload(String path, InputStream in, boolean autoClose, boolean overrideIfExist) throws Exception; + + /** + * 执行下载操作 + * + * @param path path + * @return stream + * @throws Exception Exception + */ + protected abstract InputStream doDownload(String path) throws Exception; + + /** + * 获取返回路径 用于客户端返回 + * + * @param path path + * @return returnPath + */ + protected abstract String getReturnPath(String path); + + /** + * 获取实际存储路径 用于服务端的存储 + * + * @param returnPath returnPath + * @return absolutePath + */ + protected abstract String getAbsolutePath(String returnPath); + + /** + * 获取文件路径 拼接前缀 + * + * @param path 路径 + * @return 文件名称 + */ + protected String getFilePath(String path) { + // 无需拼接 + if (!config.isNameAppendTraceId()) { + return path; + } + // 名称前缀 + String traceId = TraceIdHolder.get(); + if (traceId == null) { + traceId = UUIds.random32(); + } + String prefix = traceId + "_"; + String name = Files1.getFileName(path); + // 只是文件名 + if (name.equals(path)) { + return prefix + name; + } + // 包含路径 + String parentPath = Files1.getParentPath(path); + return parentPath + prefix + name; + } + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/FileClient.java b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/FileClient.java new file mode 100644 index 00000000..42375c15 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/FileClient.java @@ -0,0 +1,103 @@ +package com.orion.ops.framework.storage.core.client; + +import java.io.InputStream; + +/** + * 文件客户端 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/30 16:51 + */ +public interface FileClient { + + /** + * 上传文件 + * + * @param path 文件路径 + * @param content 文件内容 + * @return 路径 + * @throws Exception Exception + */ + String upload(String path, byte[] content) throws Exception; + + /** + * 上传文件 + * + * @param path 文件路径 + * @param content 文件内容 + * @param overrideIfExist 文件存在是否覆盖 + * @return 路径 + * @throws Exception Exception + */ + String upload(String path, byte[] content, boolean overrideIfExist) throws Exception; + + /** + * 上传文件 + * + * @param path 文件路径 + * @param in in + * @return 路径 + * @throws Exception Exception + */ + String upload(String path, InputStream in) throws Exception; + + /** + * 上传文件 + * + * @param path 文件路径 + * @param in in + * @param autoClose autoClose + * @return 路径 + * @throws Exception Exception + */ + String upload(String path, InputStream in, boolean autoClose) throws Exception; + + /** + * 上传文件 + * + * @param path 文件路径 + * @param in in + * @param autoClose autoClose + * @param overrideIfExist 文件存在是否覆盖 + * @return 路径 + * @throws Exception Exception + */ + String upload(String path, InputStream in, boolean autoClose, boolean overrideIfExist) throws Exception; + + /** + * 检测文件是否存在 + * + * @param path path + * @return 是否存在 + */ + boolean isExists(String path); + + /** + * 删除文件 + * + * @param path 路径 + * @return 是否删除 + * @throws Exception Exception + */ + boolean delete(String path) throws Exception; + + /** + * 获取文件内容 + * + * @param path path + * @return bytes + * @throws Exception Exception + */ + byte[] getContent(String path) throws Exception; + + /** + * 获取文件输入流 + * + * @param path path + * @return stream + * @throws Exception Exception + */ + InputStream getContentInputStream(String path) throws Exception; + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/FileClientConfig.java b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/FileClientConfig.java new file mode 100644 index 00000000..fb834d33 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/FileClientConfig.java @@ -0,0 +1,20 @@ +package com.orion.ops.framework.storage.core.client; + +import lombok.Data; + +/** + * 文件客户端配置 基类 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/30 17:21 + */ +@Data +public class FileClientConfig { + + /** + * 是否自动拼接 traceId 前缀. 没有则使用 UUID + */ + protected boolean nameAppendTraceId; + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/local/LocalFileClient.java b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/local/LocalFileClient.java new file mode 100644 index 00000000..f4675309 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/local/LocalFileClient.java @@ -0,0 +1,71 @@ +package com.orion.ops.framework.storage.core.client.local; + +import com.orion.lang.utils.io.Files1; +import com.orion.lang.utils.io.Streams; +import com.orion.ops.framework.storage.core.client.AbstractFileClient; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * 本地文件客户端 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/30 17:21 + */ +public class LocalFileClient extends AbstractFileClient { + + public LocalFileClient(LocalFileClientConfig config) { + super(config); + } + + @Override + protected String doUpload(String path, InputStream in, boolean autoClose, boolean overrideIfExist) throws Exception { + // 获取返回文件路径 + String returnPath = this.getReturnPath(path); + // 检测文件是否存在 + if (!overrideIfExist && this.isExists(returnPath)) { + return returnPath; + } + // 获取实际文件路径 + String absolutePath = this.getAbsolutePath(returnPath); + // 上传文件 + try (OutputStream out = Files1.openOutputStreamFast(absolutePath)) { + Streams.transfer(in, out); + } finally { + if (autoClose) { + Streams.close(in); + } + } + return returnPath; + } + + @Override + protected InputStream doDownload(String path) throws Exception { + return Files1.openInputStreamFast(this.getAbsolutePath(path)); + } + + @Override + public boolean isExists(String path) { + return Files1.isFile(this.getAbsolutePath(path)); + } + + @Override + public boolean delete(String path) { + return Files1.delete(this.getAbsolutePath(path)); + } + + @Override + protected String getReturnPath(String path) { + // 拼接前缀 + return Files1.getPath(config.getBasePath() + "/" + this.getFilePath(path)); + } + + @Override + protected String getAbsolutePath(String returnPath) { + // 拼接存储路径 + return Files1.getPath(config.getStoragePath() + "/" + returnPath); + } + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/local/LocalFileClientConfig.java b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/local/LocalFileClientConfig.java new file mode 100644 index 00000000..874b3045 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/java/com/orion/ops/framework/storage/core/client/local/LocalFileClientConfig.java @@ -0,0 +1,32 @@ +package com.orion.ops.framework.storage.core.client.local; + +import com.orion.ops.framework.storage.core.client.FileClientConfig; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 本地文件客户端 配置 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/30 17:47 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class LocalFileClientConfig extends FileClientConfig { + + /** + * 存储路径 + *

+ * 无需 / 结尾 + */ + private String storagePath = ""; + + /** + * 基础路径 + *

+ * 无需 / 结尾 + */ + private String basePath = ""; + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/resources/META-INF/spring-configuration-metadata.json b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/resources/META-INF/spring-configuration-metadata.json new file mode 100644 index 00000000..5816e482 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/resources/META-INF/spring-configuration-metadata.json @@ -0,0 +1,29 @@ +{ + "groups": [ + { + "name": "orion.storage", + "type": "com.orion.ops.framework.storage.config.StorageConfig", + "sourceType": "com.orion.ops.framework.storage.config.StorageConfig" + } + ], + "properties": [ + { + "name": "orion.storage.local.nameAppendTraceId", + "type": "java.lang.Boolean", + "description": "是否自动拼接 traceId 前缀. 没有则使用 UUID.", + "defaultValue": false + }, + { + "name": "orion.storage.local.storagePath", + "type": "java.lang.String", + "description": "存储路径.", + "defaultValue": "" + }, + { + "name": "orion.storage.local.basePath", + "type": "java.lang.String", + "description": "基础路径.", + "defaultValue": "" + } + ] +} \ No newline at end of file diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..f75d8775 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-storage/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.orion.ops.framework.storage.config.OrionStorageAutoConfiguration \ No newline at end of file diff --git a/orion-ops-framework/pom.xml b/orion-ops-framework/pom.xml index 93aa07b0..7b9b0ddc 100644 --- a/orion-ops-framework/pom.xml +++ b/orion-ops-framework/pom.xml @@ -27,6 +27,7 @@ orion-ops-spring-boot-starter-redis orion-ops-spring-boot-starter-desensitize orion-ops-spring-boot-starter-log + orion-ops-spring-boot-starter-storage \ No newline at end of file diff --git a/orion-ops-launch/pom.xml b/orion-ops-launch/pom.xml index ca671fe0..3181ff21 100644 --- a/orion-ops-launch/pom.xml +++ b/orion-ops-launch/pom.xml @@ -64,6 +64,11 @@ com.orion.ops orion-ops-spring-boot-starter-log + + com.orion.ops + orion-ops-spring-boot-starter-storage + ${revision} + diff --git a/orion-ops-launch/src/main/resources/application.yaml b/orion-ops-launch/src/main/resources/application.yaml index 5d6299c1..0a9fb9f1 100644 --- a/orion-ops-launch/src/main/resources/application.yaml +++ b/orion-ops-launch/src/main/resources/application.yaml @@ -139,3 +139,8 @@ orion: email: ljh1553488six@139.com license: Apache-2.0 license-url: https://github.com/lijiahangmax/orion-ops-pro/blob/main/LICENSE + storage: + local: + nameAppendTraceId: true + storagePath: ${user.home} + basePath: /orion/storage/orion-ops-pro