Browse Source

first commit

xiewj 2 years ago
parent
commit
c9d6b6547c

+ 69 - 0
4dkankan-utils-filestorage/pom.xml

@@ -0,0 +1,69 @@
+<?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>4dkankan-utils</artifactId>
+        <groupId>com.fdkankan</groupId>
+        <version>3.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>4dkankan-utils-filestorage</artifactId>
+
+    <properties>
+        <fastjson.version>2.0.6</fastjson.version>
+        <cos.version>5.6.166</cos.version>
+        <aws.version>1.12.481</aws.version>
+        <minio.version>8.2.2</minio.version>
+        <aliyun-sdk-oss.version>3.8.0</aliyun-sdk-oss.version>
+
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.2.11</version>
+        </dependency>
+        <!-- 阿里JSON解析器 -->
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+            <version>${fastjson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>${aliyun-sdk-oss.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.amazonaws</groupId>
+            <artifactId>aws-java-sdk-s3</artifactId>
+            <version>${aws.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.qcloud</groupId>
+            <artifactId>cos_api</artifactId>
+            <version>${cos.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+            <version>${minio.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-autoconfigure</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 11 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/Consumer.java

@@ -0,0 +1,11 @@
+package com.fdkankan.filestorage;
+
+/**
+ * @author Xiewj
+ * @date 2021/9/29
+ */
+
+public interface Consumer<T> {
+
+   void accept(T t);
+}

+ 57 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/InnerUtils.java

@@ -0,0 +1,57 @@
+package com.fdkankan.filestorage;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+/**
+ * @author Xiewj
+ * @date 2021/9/29
+ */
+public class InnerUtils {
+    public static String getUrlPath(String url) {
+        try {
+            String path = new URL(url).getPath();
+            return URLDecoder.decode(path, StandardCharsets.UTF_8.name());
+        } catch (MalformedURLException | UnsupportedEncodingException e) { // Ignore
+        }
+        return "";
+    }
+
+    public static String generateHost(String endpoint, String bucket) {
+        String http = "http://";
+        String https = "https://";
+        if (endpoint.startsWith(http)) {
+            return http + bucket + "." + endpoint.substring(http.length());
+        }
+        if (endpoint.startsWith(https)) {
+            return https + bucket + "." + endpoint.substring(https.length());
+        }
+        return http + bucket + "." + endpoint;
+    }
+
+
+    public static String decodeUrl(String url) {
+        try {
+            return URLDecoder.decode(url, StandardCharsets.UTF_8.name());
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("Never Happen!", e);
+        }
+    }
+
+    public static String encodeUrl(String url) {
+        try {
+            return URLEncoder.encode(url, StandardCharsets.UTF_8.name());
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("Never Happen!", e);
+        }
+    }
+
+    public static void checkArgument(boolean expect, String messageFormat, Object... args) {
+        if (!expect) {
+            String message = String.format(messageFormat, args);
+            throw new IllegalArgumentException(message);
+        }
+    }
+}

+ 91 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/OssOptions.java

@@ -0,0 +1,91 @@
+package com.fdkankan.filestorage;
+
+
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * 提供简化上传/下载的基本操作,复杂操作请使用原始客户端OSS.
+ * <p>
+ * 上传文件: uploadXxx 下载文件: downloadXxx
+ */
+public interface OssOptions  {
+
+    //------------------------------------------------------------------
+    // 上传文件
+    //------------------------------------------------------------------
+    String uploadFile(String pathKey, String filePath);
+
+    String uploadFile(String bucket, String pathKey, String filePath);
+
+    String uploadFileText(String pathKey, String text);
+
+    String uploadFileText(String bucket, String pathKey, String text);
+
+    String uploadFileBase64Image(String pathKey, String base64);
+
+    String uploadFileBase64Image(String bucket, String pathKey, String base64);
+
+    String uploadFileBytes(String pathKey, byte[] bytes);
+
+    String uploadFileBytes(String bucket, String pathKey, byte[] bytes);
+
+    String uploadFileStream(String pathKey, InputStream stream);
+
+    String uploadFileStream(String bucket, String pathKey, InputStream stream);
+
+    String uploadFileImage(String pathKey, String filePath);
+
+    String uploadFileImage(String bucket, String pathKey, String filePath);
+
+    //------------------------------------------------------------------
+    // 下载文件
+    //------------------------------------------------------------------
+
+    File downloadFileTmp(String pathKey);
+
+    File downloadFileTmp(String bucket, String pathKey);
+
+    Object downloadFile(String pathKey, String file);
+
+    Object downloadFile(String bucket, String pathKey, String file);
+
+    Object downloadFile(String pathKey, Consumer<InputStream> handler);
+
+    Object downloadFile(String bucket, String pathKey, Consumer<InputStream> handler);
+
+    //------------------------------------------------------------------
+    // 辅助方法
+    //------------------------------------------------------------------
+
+     boolean ossDownloadFileToLocal(String path,String localPath);
+      boolean ossDownloadFileToLocal(String bucket,String path, String localPath);
+    Boolean copyObject(String oldPath, String newPath);
+
+    Boolean copyObject(String bucket, String oldPath, String toBucket, String newPath);
+
+
+    void deleteObject(String keyName);
+
+    void deleteObject(String bucket, String keyName);
+
+    /**
+     * 获取文件夹
+     *
+     * @param keyName
+     * @return
+     */
+    List<String> getFileFolder(String keyName);
+
+    List<String> getFileFolder(String bucket, String keyName);
+
+    boolean doesObjectExist(String bucket, String keyName);
+
+    boolean doesObjectExist(String keyName);
+
+    String getBucket();
+    String calculateUrl(String pathKey);
+    String calculateUrl(String bucket, String pathKey);
+}

+ 121 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/StorageAutoConfiguration.java

@@ -0,0 +1,121 @@
+package com.fdkankan.filestorage;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.amazonaws.ClientConfiguration;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.regions.Regions;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3ClientBuilder;
+import com.fdkankan.filestorage.aliyun.AliyunOssTemplate;
+import com.fdkankan.filestorage.aws.AwsTemplate;
+import com.fdkankan.filestorage.cos.CosTemplate;
+import com.fdkankan.filestorage.minio.MinioTemplate;
+import com.fdkankan.filestorage.properties.AliyunOssProperties;
+import com.fdkankan.filestorage.properties.AwsProperties;
+import com.fdkankan.filestorage.properties.CosProperties;
+import com.fdkankan.filestorage.properties.MinioProperties;
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.auth.BasicCOSCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import com.qcloud.cos.http.HttpProtocol;
+import com.qcloud.cos.region.Region;
+import io.minio.MinioClient;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Xiewj
+ * @date 2023/8/28
+ */
+@Configuration
+@EnableConfigurationProperties({AliyunOssProperties.class, AwsProperties.class, CosProperties.class, MinioProperties.class})
+@ConditionalOnProperty(prefix  = "filestorage",name= "active")
+public class StorageAutoConfiguration {
+    @Bean
+    @ConditionalOnProperty(name = "filestorage.active",havingValue = "oss")
+    public OSS aliyunOssClient(AliyunOssProperties properties) {
+        return new OSSClientBuilder().build(properties.getRequestEndpoint(),
+                properties.getAccessKey(),
+                properties.getAccessKeySecret(),
+                properties.getConfig());
+    }
+    @Bean
+    @ConditionalOnProperty(name = "filestorage.active",havingValue = "oss")
+    public AliyunOssTemplate aliyunOssTemplate(OSS oss, AliyunOssProperties properties) {
+        return new  AliyunOssTemplate(oss, properties);
+    }
+
+
+    @Bean
+    @ConditionalOnProperty(name = "filestorage.active",havingValue = "awss3")
+    public AmazonS3 amazonS3Client(AwsProperties properties) {
+        BasicAWSCredentials awsCreds = new BasicAWSCredentials(properties.getAccessKey(), properties.getAccessKeySecret());
+        ClientConfiguration clientConfiguration = new ClientConfiguration();
+        clientConfiguration.setMaxErrorRetry(4);
+        clientConfiguration.setMaxConnections(100);
+        AmazonS3 s3 = AmazonS3ClientBuilder.standard()
+                .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
+                .withRegion(Regions.EU_WEST_2)
+                .withClientConfiguration(clientConfiguration)
+                .build();
+        return s3;
+    }
+
+
+
+    @Bean
+    @ConditionalOnProperty(name = "filestorage.active",havingValue = "awss3")
+    public AwsTemplate AwsOssTemplate(AmazonS3 oss, AwsProperties properties) {
+        return new AwsTemplate(oss, properties);
+    }
+
+    @Bean
+    @ConditionalOnProperty(name = "filestorage.active",havingValue = "cos")
+    public COSClient cosClient(CosProperties properties) {
+
+        // 1 初始化用户身份信息(secretId, secretKey)。
+        // SECRETID 和 SECRETKEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理
+        COSCredentials cred = new BasicCOSCredentials(properties.getAccessKey(), properties.getAccessKeySecret());
+        // 2 设置 bucket 的地域, COS 地域的简称请参见 https://cloud.tencent.com/document/product/436/6224
+        // clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。
+        Region region = new Region(properties.getRegionId());
+        ClientConfig clientConfig = properties.getConfig();
+        clientConfig.setRegion(region);
+        // 这里建议设置使用 https 协议
+        // 从 5.6.54 版本开始,默认使用了 https
+        clientConfig.setHttpProtocol(HttpProtocol.https);
+        // 3 生成 cos 客户端。
+        COSClient cosClient = new COSClient(cred, clientConfig);
+
+        return cosClient;
+    }
+
+
+
+    @Bean
+    @ConditionalOnProperty(name = "filestorage.active",havingValue = "cos")
+    public CosTemplate cosTemplate(COSClient ossClient, CosProperties properties) {
+        return new CosTemplate(ossClient, properties);
+    }
+    @Bean
+    @ConditionalOnProperty(name = "filestorage.active",havingValue = "minio")
+    public MinioClient minioClient(MinioProperties properties) {
+        return MinioClient.builder()
+                .endpoint(properties.getEndpoint())
+                .credentials(properties.getAccessKey(), properties.getAccessKeySecret())
+                .build();
+    }
+
+
+    @Bean
+    @ConditionalOnProperty(name = "filestorage.active",havingValue = "minio")
+    public MinioTemplate minioOssTemplate(MinioClient oss, MinioProperties properties) {
+        return new MinioTemplate(oss, properties);
+    }
+
+}

+ 337 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/aliyun/AliyunOssTemplate.java

@@ -0,0 +1,337 @@
+package com.fdkankan.filestorage.aliyun;
+
+
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.ServiceException;
+import com.aliyun.oss.model.*;
+import com.fdkankan.filestorage.Consumer;
+import com.fdkankan.filestorage.InnerUtils;
+import com.fdkankan.filestorage.OssOptions;
+import com.fdkankan.filestorage.properties.AliyunOssProperties;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Base64Utils;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+
+/**
+ * 阿里云操作模版类, 简化常见操作
+ */
+@Getter
+@Slf4j
+@Service("AliyunOssTemplate")
+@ConditionalOnProperty(prefix = AliyunOssProperties.PREFIX,name = "filestorage.active",havingValue = "oss")
+public class AliyunOssTemplate implements OssOptions {
+
+    private final OSS ossClient;
+    private final AliyunOssProperties ossProperties;
+
+    public AliyunOssTemplate(OSS ossClient, AliyunOssProperties ossProperties) {
+        this.ossClient = ossClient;
+        this.ossProperties = ossProperties;
+    }
+
+    @Override
+    public String uploadFile(String pathKey, String filePath) {
+        return uploadFile(ossProperties.getBucket(), pathKey, filePath);
+    }
+
+    @Override
+    public String uploadFile(String bucket, String pathKey, String filePath) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        ObjectMetadata metadata = new ObjectMetadata();
+        ossClient.putObject(bucket, pathKey, new File(filePath), metadata);
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public String uploadFileText(String pathKey, String content) {
+        return uploadFileText(ossProperties.getBucket(), pathKey, content);
+    }
+
+    @Override
+    public String uploadFileText(String bucket, String pathKey, String content) {
+        byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
+        return uploadFileBytes(bucket, pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBase64Image(String pathKey, String content) {
+        return uploadFileBase64Image(ossProperties.getBucket(), pathKey, content);
+    }
+
+    @Override
+    public String uploadFileBase64Image(String bucket, String pathKey, String content) {
+        byte[] bytes = Base64Utils.decodeFromString(content);
+        return uploadFileBytes(bucket, pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBytes(String pathKey, byte[] bytes) {
+        return uploadFileBytes(ossProperties.getBucket(), pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBytes(String bucket, String pathKey, byte[] bytes) {
+        ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
+        return uploadFileStream(bucket, pathKey, stream);
+    }
+
+    @Override
+    public String uploadFileStream(String pathKey, InputStream stream) {
+        return uploadFileStream(ossProperties.getBucket(), pathKey, stream);
+    }
+
+    @Override
+    public String uploadFileStream(String bucket, String pathKey, InputStream stream) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(stream != null, "stream can't be null");
+
+        ObjectMetadata metadata = new ObjectMetadata();
+        ossClient.putObject(bucket, pathKey, stream, metadata);
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public File downloadFileTmp(String pathKey) {
+        return downloadFileTmp(ossProperties.getBucket(), pathKey);
+    }
+
+    @Override
+    public File downloadFileTmp(String bucket, String pathKey) {
+        String tmpDir = System.getProperty("java.io.tmpdir");
+        String file = tmpDir + UUID.randomUUID();
+        downloadFile(bucket, pathKey, file);
+        return new File(file);
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String pathKey, String file) {
+        return downloadFile(ossProperties.getBucket(), pathKey, file);
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String bucket, String pathKey, String file) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(file != null && file.length() > 0, "file can't be empty");
+        GetObjectRequest request = new GetObjectRequest(bucket, pathKey);
+        log.info("下载开始:下载bucket={},下载pathKey={},下载filePath={}", bucket, pathKey, file);
+        return ossClient.getObject(request, new File(file));
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String pathKey, Consumer<InputStream> handler) {
+        return downloadFile(ossProperties.getBucket(), pathKey, handler);
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String bucket, String pathKey, Consumer<InputStream> handler) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(handler != null, "handler can't be null");
+        OSSObject ossObject = ossClient.getObject(bucket, pathKey);
+        InputStream content = ossObject.getObjectContent();
+        try {
+            handler.accept(content);
+        } finally {
+            if (content != null) {
+                try {
+                    content.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+        return ossObject.getObjectMetadata();
+    }
+
+    @Override
+    public boolean ossDownloadFileToLocal(String bucket,String path, String localPath) {
+        File file = new File(localPath);
+        if (!file.getParentFile().exists()){
+            file.getParentFile().mkdirs();
+        }
+
+        log.info("localPath : " + localPath);
+
+        if("/".equals(path.substring(0,1))){
+            path = path.substring(1);
+        }
+        try (FileOutputStream fos = new FileOutputStream(file)){
+            // 下载OSS文件流
+            OSSObject object = ossClient.getObject(bucket, path);
+
+            InputStream is = object.getObjectContent();
+            byte[] b = new byte[1024];
+            int length;
+            while((length= is.read(b)) != -1){
+                fos.write(b,0,length);
+            }
+
+            is.close();
+
+            // 关闭OSSClient。
+        }catch (IOException e){
+            throw new ServiceException(e.getMessage());
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean ossDownloadFileToLocal(String path, String localPath) {
+        return ossDownloadFileToLocal(ossProperties.getBucket(),path,localPath);
+    }
+
+    @Override
+    public String calculateUrl(String pathKey) {
+        return calculateUrl(ossProperties.getBucket(), pathKey);
+    }
+
+    @Override
+    public String calculateUrl(String bucket, String pathKey) {
+        String host = ossProperties.getHostByBucket(bucket);
+        return host + "/" + pathKey;
+    }
+
+    public String calculatePathKey(String url) {
+        String urlPath = InnerUtils.getUrlPath(url);
+        if (urlPath != null && urlPath.startsWith("/")) {
+            String pathKey = urlPath.substring(1);
+            if (pathKey.length() > 0) {
+                return InnerUtils.decodeUrl(pathKey);
+            }
+        }
+        return null;
+    }
+
+    public String calculateHost(String endpoint, String bucket) {
+        return InnerUtils.generateHost(endpoint, bucket);
+    }
+
+
+    @Override
+    public Boolean copyObject(String oldPath, String newPath) {
+        return copyObject(ossProperties.getBucket(), oldPath, ossProperties.getBucket(), newPath);
+    }
+
+    @Override
+    public Boolean copyObject(String bucket, String oldPath, String toBucket, String newPath) {
+        log.info("拷贝开始:oldBucket={},oldPath={},newBucket={},newPath={}", bucket, oldPath, toBucket, newPath);
+        String versionId = ossClient.copyObject(bucket, oldPath, toBucket, newPath).getVersionId();
+        if (StrUtil.isNotBlank(versionId)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void deleteObject(String keyName) {
+        if (doesObjectExist(keyName)) {
+            deleteObject(ossProperties.getBucket(), keyName);
+        } else {
+            log.warn("文件不存在,KeyName{}", keyName);
+        }
+    }
+
+    @Override
+    public void deleteObject(String bucket, String keyName) {
+        ossClient.deleteObject(bucket, keyName);
+    }
+
+    @Override
+    public List<String> getFileFolder(String keyName) {
+        return getFileFolder(ossProperties.getBucket(), keyName);
+    }
+
+    @Override
+    public List<String> getFileFolder(String bucket, String keyName) {
+        log.info("获取文件夹bucket-{},路径{}",bucket,keyName);
+        List<String> keyList = new ArrayList<>();
+        try {
+            boolean flag = true;
+            String nextMaker = null;
+            //指定下一级文件
+            ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucket);
+            // 设置正斜线(/)为文件夹的分隔符。
+            listObjectsRequest.setDelimiter("/");
+            // 设置prefix参数来获取fun目录下的所有文件。
+            if (!StringUtils.isEmpty(keyName)) {
+                listObjectsRequest.setPrefix(keyName + "/");
+            }
+            //设置分页的页容量
+            listObjectsRequest.setMaxKeys(200);
+            do {
+                //获取下一页的起始点,它的下一项
+                listObjectsRequest.setMarker(nextMaker);
+                ObjectListing objectListing = ossClient.listObjects(listObjectsRequest);
+                List<String> collect = objectListing.getObjectSummaries().parallelStream()
+                        .map(OSSObjectSummary::getKey).collect(Collectors.toList());
+                if (!CollectionUtils.isEmpty(collect)) {
+                    keyList.addAll(collect);
+                }
+                nextMaker = objectListing.getNextMarker();
+                //全部执行完后,为false
+                flag = objectListing.isTruncated();
+            } while (flag);
+        } catch (Exception e) {
+            log.error("获取文件列表失败,path=" + keyName, e);
+            e.printStackTrace();
+        }
+        log.info("获取文件夹集合={}", JSON.toJSONString(keyList));
+        return keyList;
+    }
+
+    @Override
+    public boolean doesObjectExist(String bucket, String keyName) {
+        boolean b = ossClient.doesObjectExist(bucket, keyName);
+        log.info("文件是否存在={}", b);
+        return b;
+    }
+
+    @Override
+    public boolean doesObjectExist(String keyName) {
+        return doesObjectExist(ossProperties.getBucket(), keyName);
+    }
+
+    @Override
+    public String getBucket() {
+        return ossProperties.getBucket();
+    }
+
+    @Override
+    public String uploadFileImage(String pathKey, String filePath) {
+        return uploadFileImage(ossProperties.getBucket(), pathKey, filePath);
+    }
+
+    @Override
+    public String uploadFileImage(String bucket, String pathKey, String filePath) {
+        ObjectMetadata metadata = new ObjectMetadata();
+        if (filePath.contains(".jpg")) {
+            metadata.setContentType("image/jpeg");
+        } else if (filePath.contains(".png")) {
+            metadata.setContentType("image/png");
+        }
+        log.info("上传开始:上传bucket={},上传pathKey={},上传filePath={}", bucket, pathKey, filePath);
+        ossClient.putObject(bucket, pathKey, new File(filePath), metadata);
+        return calculateUrl(bucket, pathKey);
+    }
+
+
+}

+ 390 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/aws/AwsTemplate.java

@@ -0,0 +1,390 @@
+package com.fdkankan.filestorage.aws;
+
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
+import com.amazonaws.AmazonServiceException;
+import com.amazonaws.SdkClientException;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.model.*;
+import com.fdkankan.filestorage.Consumer;
+import com.fdkankan.filestorage.InnerUtils;
+import com.fdkankan.filestorage.OssOptions;
+import com.fdkankan.filestorage.properties.AwsProperties;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Base64Utils;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+
+/**
+ * 阿里云操作模版类, 简化常见操作
+ */
+@Getter
+@Slf4j
+@Service("AwsOssTemplate")
+@ConditionalOnProperty(prefix = AwsProperties.PREFIX,name = "filestorage.active",havingValue = "awss3")
+public class AwsTemplate implements OssOptions {
+
+    private final AmazonS3 amazonS3Client;
+    private final AwsProperties ossProperties;
+
+    public AwsTemplate(AmazonS3 amazonS3Client, AwsProperties ossProperties) {
+        this.amazonS3Client = amazonS3Client;
+        this.ossProperties = ossProperties;
+    }
+
+    @Override
+    public String uploadFile(String pathKey, String filePath) {
+        return uploadFile(ossProperties.getBucket(), pathKey, filePath);
+    }
+
+    @Override
+    public String uploadFile(String bucket, String pathKey, String filePath) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        uploadFileStream(bucket, pathKey, FileUtil.getInputStream(new File(filePath)));
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public String uploadFileText(String pathKey, String content) {
+        return uploadFileText(ossProperties.getBucket(), pathKey, content);
+    }
+
+    @Override
+    public String uploadFileText(String bucket, String pathKey, String content) {
+        byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
+        return uploadFileBytes(bucket, pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBase64Image(String pathKey, String content) {
+        return uploadFileBase64Image(ossProperties.getBucket(), pathKey, content);
+    }
+
+    @Override
+    public String uploadFileBase64Image(String bucket, String pathKey, String content) {
+        byte[] bytes = Base64Utils.decodeFromString(content);
+        return uploadFileBytes(bucket, pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBytes(String pathKey, byte[] bytes) {
+        return uploadFileBytes(ossProperties.getBucket(), pathKey, bytes);
+    }
+
+    public void createbucket() {
+        try {
+            if (StrUtil.isEmpty(ossProperties.getBucket())) {
+                throw new IllegalArgumentException("桶名称不能为空!");
+            }
+            try {
+                amazonS3Client.createBucket(ossProperties.getBucket());
+            } catch (SdkClientException e) {
+                e.printStackTrace();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public String uploadFileBytes(String bucket, String pathKey, byte[] bytes) {
+        ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
+        return uploadFileStream(bucket, pathKey, stream);
+    }
+
+    @Override
+    public String uploadFileStream(String pathKey, InputStream stream) {
+        return uploadFileStream(ossProperties.getBucket(), pathKey, stream);
+    }
+
+    @Override
+    public String uploadFileStream(String bucket, String pathKey, InputStream stream) {
+
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(stream != null, "stream can't be null");
+        try {
+            ObjectMetadata metadata = new ObjectMetadata();
+            if (pathKey.contains(".jpg")) {
+                metadata.setContentType("image/jpeg");
+            } else if (pathKey.contains(".png")) {
+                metadata.setContentType("image/png");
+            } else if (pathKey.contains(".json")) {
+                metadata.setContentType("application/json");
+
+            }
+            PutObjectRequest request = new PutObjectRequest(bucket, pathKey,stream ,metadata);
+            request.withCannedAcl(CannedAccessControlList.PublicRead);
+            PutObjectResult putObjectResult = amazonS3Client.putObject(request);
+            if (StrUtil.isNotEmpty(putObjectResult.getETag())) {
+                log.info("s3上传文件成功:" + pathKey);
+            }
+
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public File downloadFileTmp(String pathKey) {
+        return downloadFileTmp(ossProperties.getBucket(), pathKey);
+    }
+
+    @Override
+    public File downloadFileTmp(String bucket, String pathKey) {
+        String tmpDir = System.getProperty("java.io.tmpdir");
+        String file = tmpDir + UUID.randomUUID();
+        downloadFile(bucket, pathKey, file);
+        return new File(file);
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String pathKey, String file) {
+        return downloadFile(ossProperties.getBucket(), pathKey, file);
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String bucket, String pathKey, String file) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(file != null && file.length() > 0, "file can't be empty");
+        GetObjectRequest request  = new GetObjectRequest(bucket,pathKey);
+        log.info("下载开始:下载bucket={},下载pathKey={},下载filePath={}", bucket, pathKey, file);
+        try {
+
+            return amazonS3Client.getObject(request,new File(file));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String pathKey, Consumer<InputStream> handler) {
+        return downloadFile(ossProperties.getBucket(), pathKey, handler);
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String bucket, String pathKey, Consumer<InputStream> handler) {
+
+
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(handler != null, "handler can't be null");
+        GetObjectRequest request  = new GetObjectRequest(bucket,pathKey);
+        S3Object object = amazonS3Client.getObject(request);
+        InputStream content = object.getObjectContent();
+        try {
+            handler.accept(content);
+        } finally {
+            if (content != null) {
+                try {
+                    content.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+        return object.getObjectMetadata();
+    }
+
+    public String calculateUrl(String pathKey) {
+        return calculateUrl(ossProperties.getBucket(), pathKey);
+    }
+
+    public String calculateUrl(String bucket, String pathKey) {
+        String host = ossProperties.getHostByBucket(bucket);
+        return host + "/" + pathKey;
+    }
+
+    public String calculatePathKey(String url) {
+        String urlPath = InnerUtils.getUrlPath(url);
+        if (urlPath != null && urlPath.startsWith("/")) {
+            String pathKey = urlPath.substring(1);
+            if (pathKey.length() > 0) {
+                return InnerUtils.decodeUrl(pathKey);
+            }
+        }
+        return null;
+    }
+
+    public String calculateHost(String endpoint, String bucket) {
+        return InnerUtils.generateHost(endpoint, bucket);
+    }
+
+
+    @Override
+    public Boolean copyObject(String oldPath, String newPath) {
+        return copyObject(ossProperties.getBucket(), oldPath, ossProperties.getBucket(), newPath);
+    }
+
+    @Override
+    public Boolean copyObject(String bucket, String oldPath, String toBucket, String newPath) {
+        try {
+            List<String> sourceKeyList = this.getFileFolder(oldPath);
+
+            // 复制文件
+            sourceKeyList.parallelStream().forEach(key -> {
+                log.info("开始复制:" + key);
+                amazonS3Client.copyObject(bucket, key, toBucket, key.replace(oldPath, newPath));
+                log.info("复制成功:" + key);
+            });
+            CopyObjectResult copyObjectResult = amazonS3Client.copyObject(bucket, oldPath, toBucket, newPath);
+
+            if (StrUtil.isNotBlank(copyObjectResult.getVersionId())) {
+                return true;
+            }
+            return false;
+        } catch (Exception e) {
+            log.error("文件不存在,KeyName{}", e.getMessage());
+            return false;
+        }
+    }
+
+    @Override
+    public void deleteObject(String keyName) {
+        if (doesObjectExist(keyName)) {
+            deleteObject(ossProperties.getBucket(), keyName);
+        } else {
+            log.warn("文件不存在,KeyName{}", keyName);
+        }
+    }
+
+    @Override
+    public void deleteObject(String bucket, String keyName) {
+        try {
+            InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+            InnerUtils.checkArgument(keyName != null && keyName.length() > 0, "keyName can't be empty");
+            amazonS3Client.deleteObject(bucket, keyName);
+        } catch (Exception e) {
+            log.info("文件删除失败" + e.getMessage());
+        }
+    }
+
+    @Override
+    public List<String> getFileFolder(String keyName) {
+        return getFileFolder(ossProperties.getBucket(), keyName);
+    }
+
+    @Override
+    public List<String> getFileFolder(String bucket, String keyName) {
+        List<String> list = new ArrayList<>();
+        try {
+            String nextMaker = null;
+            boolean flag = true;
+            ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
+            listObjectsRequest.setBucketName(bucket);
+            listObjectsRequest.setPrefix(keyName);
+            listObjectsRequest.setMaxKeys(200);
+            do{
+                listObjectsRequest.setMarker(nextMaker);
+                ObjectListing objectListing = amazonS3Client.listObjects(listObjectsRequest);
+                List<S3ObjectSummary> objectSummaries = objectListing.getObjectSummaries();
+                List<String> collect =objectSummaries.stream().map(summary->{
+                    return summary.getKey();
+                }).collect(Collectors.toList());
+                if(CollUtil.isNotEmpty(collect)){
+                    list.addAll(collect);
+                }
+                nextMaker = objectListing.getNextMarker();
+                flag = objectListing.isTruncated();
+            }while (flag);
+            log.info("获取文件夹集合={}", JSON.toJSONString(list));
+        } catch (Exception e) {
+            log.error("获取文件夹集合失败={}", e.getMessage());
+        }
+
+        return list;
+    }
+
+    @Override
+    public boolean doesObjectExist(String bucket, String keyName) {
+        try {
+            GetObjectRequest request  = new GetObjectRequest(bucket,keyName);
+            S3Object object = amazonS3Client.getObject(request);
+            return true;
+        } catch (AmazonServiceException e) {
+            if (e.getErrorCode().equalsIgnoreCase("NoSuchKey")) {
+                log.error("NoSuchKey", e.getMessage());
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("连接异常", e.getMessage());
+            return false;
+        }
+        return false;
+    }
+    @Override
+    public String getBucket() {
+        return ossProperties.getBucket();
+    }
+
+    @Override
+    public boolean ossDownloadFileToLocal(String bucket,String keyName, String localPath) {
+        try {
+            GetObjectRequest request  = new GetObjectRequest(bucket,keyName);
+            amazonS3Client.getObject(request,new File(localPath));
+            return true;
+        } catch (Exception e) {
+            log.error("amazonS3下载文件失败,key=" + amazonS3Client, e);
+        }finally {
+        }
+        return false;
+    }
+
+    @Override
+    public boolean ossDownloadFileToLocal(String path, String localPath) {
+        return ossDownloadFileToLocal(ossProperties.getBucket(),path,localPath);
+    }
+
+    @Override
+    public boolean doesObjectExist(String keyName) {
+        return doesObjectExist(ossProperties.getBucket(), keyName);
+    }
+
+    @Override
+    public String uploadFileImage(String pathKey, String filePath) {
+        return uploadFileImage(ossProperties.getBucket(), pathKey, filePath);
+    }
+
+    @Override
+    public String uploadFileImage(String bucket, String pathKey, String filePath) {
+        InputStream stream = null;
+        try {
+            ObjectMetadata metadata = new ObjectMetadata();
+            if (pathKey.contains(".jpg")) {
+                metadata.setContentType("image/jpeg");
+            } else if (pathKey.contains(".png")) {
+                metadata.setContentType("image/png");
+            } else if (pathKey.contains(".json")) {
+                metadata.setContentType("application/json");
+            }
+            stream = FileUtil.getInputStream(new File(filePath));
+            PutObjectRequest request = new PutObjectRequest(bucket, pathKey,stream ,metadata);
+            request.withCannedAcl(CannedAccessControlList.PublicRead);
+            PutObjectResult putObjectResult = amazonS3Client.putObject(request);
+        } catch (Exception e) {
+            return "";
+        } finally {
+            IoUtil.close(stream);
+        }
+        return calculateUrl(bucket, pathKey);
+    }
+
+
+}

+ 353 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/cos/CosTemplate.java

@@ -0,0 +1,353 @@
+package com.fdkankan.filestorage.cos;
+
+
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
+import com.fdkankan.filestorage.Consumer;
+import com.fdkankan.filestorage.InnerUtils;
+import com.fdkankan.filestorage.OssOptions;
+import com.fdkankan.filestorage.properties.CosProperties;
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.model.*;
+import com.qcloud.cos.utils.Md5Utils;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Base64Utils;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+
+/**
+ * 阿里云操作模版类, 简化常见操作
+ */
+@Getter
+@Slf4j
+@Service("CosTemplate")
+@ConditionalOnProperty(prefix = CosProperties.PREFIX,name = "filestorage.active",havingValue = "cos")
+public class CosTemplate implements OssOptions {
+
+    private final COSClient cosClient;
+    private final CosProperties ossProperties;
+
+    public CosTemplate(COSClient ossClient, CosProperties ossProperties) {
+        this.cosClient = ossClient;
+        this.ossProperties = ossProperties;
+    }
+
+    @Override
+    public String uploadFile(String pathKey, String filePath) {
+        return uploadFile(ossProperties.getBucket(), pathKey, filePath);
+    }
+
+    @Override
+    public String uploadFile(String bucket, String pathKey, String filePath) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        File file = new File(filePath);
+        if (!file.exists()) {
+            log.warn("要上传的文件不存在,filePath" + filePath);
+            return null;
+        }
+        try (InputStream ins = new FileInputStream(file)){
+            ObjectMetadata metadata = new ObjectMetadata();
+            if (filePath.contains(".jpg")) {
+                metadata.setContentType("image/jpeg");
+            } else if (filePath.contains(".png")) {
+                metadata.setContentType("image/png");
+            }else  if (filePath.contains(".mp4")) {
+                metadata.setContentType("video/mp4");
+            } else if (filePath.contains(".mp3")) {
+                metadata.setContentType("audio/mp3");
+            }
+            cosClient.putObject(bucket, pathKey, ins, metadata);
+            log.info("文件上传成功,path:{}", filePath);
+        } catch (Exception e) {
+            log.error("cos上传文件失败,filePath:"+filePath, e);
+        }
+
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public String uploadFileText(String pathKey, String content) {
+        return uploadFileText(ossProperties.getBucket(), pathKey, content);
+    }
+
+    @Override
+    public String uploadFileText(String bucket, String pathKey, String content) {
+        byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
+        return uploadFileBytes(bucket, pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBase64Image(String pathKey, String content) {
+        return uploadFileBase64Image(ossProperties.getBucket(), pathKey, content);
+    }
+
+    @Override
+    public String uploadFileBase64Image(String bucket, String pathKey, String content) {
+        byte[] bytes = Base64Utils.decodeFromString(content);
+        return uploadFileBytes(bucket, pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBytes(String pathKey, byte[] bytes) {
+        return uploadFileBytes(ossProperties.getBucket(), pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBytes(String bucket, String pathKey, byte[] bytes) {
+        ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
+        return uploadFileStream(bucket, pathKey, stream);
+    }
+
+    @Override
+    public String uploadFileStream(String pathKey, InputStream stream) {
+        return uploadFileStream(ossProperties.getBucket(), pathKey, stream);
+    }
+
+    @Override
+    public String uploadFileStream(String bucket, String pathKey, InputStream stream) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(stream != null, "stream can't be null");
+        ObjectMetadata metadata = new ObjectMetadata();
+        cosClient.putObject(bucket, pathKey, stream,metadata);
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public File downloadFileTmp(String pathKey) {
+        return downloadFileTmp(ossProperties.getBucket(), pathKey);
+    }
+
+    @Override
+    public File downloadFileTmp(String bucket, String pathKey) {
+        String tmpDir = System.getProperty("java.io.tmpdir");
+        String file = tmpDir + UUID.randomUUID();
+        downloadFile(bucket, pathKey, file);
+        return new File(file);
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String pathKey, String file) {
+        return downloadFile(ossProperties.getBucket(), pathKey, file);
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String bucket, String pathKey, String file) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(file != null && file.length() > 0, "file can't be empty");
+        GetObjectRequest getObjectRequest = new  GetObjectRequest(bucket, pathKey);
+        log.info("下载开始:下载bucket={},下载pathKey={},下载filePath={}", bucket, pathKey, file);
+        return cosClient.getObject(getObjectRequest, new File(file));
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String pathKey, Consumer<InputStream> handler) {
+        return downloadFile(ossProperties.getBucket(), pathKey, handler);
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String bucket, String pathKey, Consumer<InputStream> handler) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(handler != null, "handler can't be null");
+        COSObject COSObject = cosClient.getObject(bucket, pathKey);
+        InputStream content = COSObject.getObjectContent();
+        try {
+            handler.accept(content);
+        } finally {
+            if (content != null) {
+                try {
+                    content.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+        return COSObject.getObjectMetadata();
+    }
+
+    @Override
+    public boolean ossDownloadFileToLocal(String bucket,String path, String localPath) {
+        File file = new File(localPath);
+        if (!file.getParentFile().exists()){
+            file.getParentFile().mkdirs();
+        }
+
+        log.info("localPath : " + localPath);
+
+        if("/".equals(path.substring(0,1))){
+            path = path.substring(1);
+        }
+        GetObjectRequest getObjectRequest = new GetObjectRequest(bucket, path);
+        cosClient.getObject(getObjectRequest, new File(localPath));
+
+        return true;
+    }
+
+    @Override
+    public boolean ossDownloadFileToLocal(String path, String localPath) {
+        return ossDownloadFileToLocal(ossProperties.getBucket(),path,localPath);
+    }
+
+    @Override
+    public String calculateUrl(String pathKey) {
+        return calculateUrl(ossProperties.getBucket(), pathKey);
+    }
+
+    @Override
+    public String calculateUrl(String bucket, String pathKey) {
+        String host = ossProperties.getHostByBucket(bucket);
+        return host + "/" + pathKey;
+    }
+
+    public String calculatePathKey(String url) {
+        String urlPath = InnerUtils.getUrlPath(url);
+        if (urlPath != null && urlPath.startsWith("/")) {
+            String pathKey = urlPath.substring(1);
+            if (pathKey.length() > 0) {
+                return InnerUtils.decodeUrl(pathKey);
+            }
+        }
+        return null;
+    }
+
+    public String calculateHost(String endpoint, String bucket) {
+        return InnerUtils.generateHost(endpoint, bucket);
+    }
+
+
+    @Override
+    public Boolean copyObject(String oldPath, String newPath) {
+        return copyObject(ossProperties.getBucket(), oldPath, ossProperties.getBucket(), newPath);
+    }
+
+    @Override
+    public Boolean copyObject(String bucket, String oldPath, String toBucket, String newPath) {
+        log.info("拷贝开始:oldBucket={},oldPath={},newBucket={},newPath={}", bucket, oldPath, toBucket, newPath);
+        String versionId = cosClient.copyObject(bucket, oldPath, toBucket, newPath).getVersionId();
+        if (StrUtil.isNotBlank(versionId)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void deleteObject(String keyName) {
+        if (doesObjectExist(keyName)) {
+            deleteObject(ossProperties.getBucket(), keyName);
+        } else {
+            log.warn("文件不存在,KeyName{}", keyName);
+        }
+    }
+
+    @Override
+    public void deleteObject(String bucket, String keyName) {
+        cosClient.deleteObject(bucket, keyName);
+    }
+
+    @Override
+    public List<String> getFileFolder(String keyName) {
+        return getFileFolder(ossProperties.getBucket(), keyName);
+    }
+
+    @Override
+    public List<String> getFileFolder(String bucket, String keyName) {
+        log.info("获取文件夹bucket-{},路径{}",bucket,keyName);
+        List<String> keyList = new ArrayList<>();
+        long totalSubFileNum = 0;
+        try {
+            if (!keyName.endsWith("/")) {
+                keyName = keyName + "/";
+            }
+            boolean flag = true;
+            String nextMaker = null;
+            ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
+            listObjectsRequest.setBucketName(bucket);
+            //指定下一级文件
+            listObjectsRequest.setPrefix(keyName);
+            //设置分页的页容量
+            listObjectsRequest.setMaxKeys(1000);
+            do {
+                //获取下一页的起始点,它的下一项
+                listObjectsRequest.setMarker(nextMaker);
+                ObjectListing objectListing = cosClient.listObjects(listObjectsRequest);
+                List<String> collect = objectListing.getObjectSummaries().parallelStream()
+                        .filter(summary -> !summary.getKey().endsWith("/")).map(COSObjectSummary::getKey).collect(Collectors.toList());
+                if (!CollectionUtils.isEmpty(collect)) {
+                    totalSubFileNum = totalSubFileNum + collect.size();
+                }
+                nextMaker = objectListing.getNextMarker();
+                //全部执行完后,为false
+                flag = objectListing.isTruncated();
+            } while (flag);
+        } catch (Exception e) {
+            log.error("获取文件列表失败,path=" + keyName, e);
+            e.printStackTrace();
+        }
+        log.info("获取文件夹集合={}", JSON.toJSONString(keyList));
+        return keyList;
+    }
+
+    @Override
+    public boolean doesObjectExist(String bucket, String keyName) {
+        boolean b = cosClient.doesObjectExist(bucket, keyName);
+        log.info("文件是否存在={}", b);
+        return b;
+    }
+
+    @Override
+    public boolean doesObjectExist(String keyName) {
+        return doesObjectExist(ossProperties.getBucket(), keyName);
+    }
+
+    @Override
+    public String getBucket() {
+        return ossProperties.getBucket();
+    }
+
+    @Override
+    public String uploadFileImage(String pathKey, String filePath) {
+        return uploadFileImage(ossProperties.getBucket(), pathKey, filePath);
+    }
+
+    @Override
+    public String uploadFileImage(String bucket, String pathKey, String filePath) {
+        File file=new File(filePath);
+        if (!file.exists()) {
+            log.warn("要上传的文件不存在,filePath" + filePath);
+            return null;
+        }
+        try (InputStream ins = new FileInputStream(file)){
+            ObjectMetadata metadata = new  ObjectMetadata();
+            if (filePath.contains(".jpg")) {
+                metadata.setContentType("image/jpeg");
+            } else if (filePath.contains(".png")) {
+                metadata.setContentType("image/png");
+            }
+            log.info("上传开始:上传bucket={},上传pathKey={},上传filePath={}", bucket, pathKey, filePath);
+            cosClient.putObject(bucket, pathKey, ins, metadata);
+            log.info("文件上传成功,path:{}", filePath);
+        } catch (Exception e) {
+            log.error("cos上传文件失败,filePath:"+filePath, e);
+        }
+        
+        
+        return calculateUrl(bucket, pathKey);
+    }
+
+
+}

+ 364 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/minio/MinioTemplate.java

@@ -0,0 +1,364 @@
+package com.fdkankan.filestorage.minio;
+
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
+import com.fdkankan.filestorage.Consumer;
+import com.fdkankan.filestorage.InnerUtils;
+import com.fdkankan.filestorage.properties.MinioProperties;
+
+import com.fdkankan.filestorage.OssOptions;
+import io.minio.*;
+import io.minio.errors.*;
+import io.minio.messages.Item;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Base64Utils;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import static com.fdkankan.filestorage.properties.MinioProperties.PREFIX;
+
+/**
+ * 阿里云操作模版类, 简化常见操作
+ */
+@Getter
+@Slf4j
+@Service("MinioOssTemplate")
+@ConditionalOnProperty(prefix = MinioProperties.PREFIX,name = "filestorage.active",havingValue = "minio")
+public class MinioTemplate implements OssOptions {
+
+    private final MinioClient minioClient;
+    private final MinioProperties ossProperties;
+
+    public MinioTemplate(MinioClient minioClient, MinioProperties ossProperties) {
+        this.minioClient = minioClient;
+        this.ossProperties = ossProperties;
+    }
+
+    @Override
+    public String uploadFile(String pathKey, String filePath) {
+        return uploadFile(ossProperties.getBucket(), pathKey, filePath);
+    }
+
+    @Override
+    public String uploadFile(String bucket, String pathKey, String filePath) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        uploadFileStream(bucket, pathKey, FileUtil.getInputStream(new File(filePath)));
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public String uploadFileText(String pathKey, String content) {
+        return uploadFileText(ossProperties.getBucket(), pathKey, content);
+    }
+
+    @Override
+    public String uploadFileText(String bucket, String pathKey, String content) {
+        byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
+        return uploadFileBytes(bucket, pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBase64Image(String pathKey, String content) {
+        return uploadFileBase64Image(ossProperties.getBucket(), pathKey, content);
+    }
+
+    @Override
+    public String uploadFileBase64Image(String bucket, String pathKey, String content) {
+        byte[] bytes = Base64Utils.decodeFromString(content);
+        return uploadFileBytes(bucket, pathKey, bytes);
+    }
+
+    @Override
+    public String uploadFileBytes(String pathKey, byte[] bytes) {
+        return uploadFileBytes(ossProperties.getBucket(), pathKey, bytes);
+    }
+
+    public void createbucket() {
+        try {
+            minioClient.makeBucket(MakeBucketArgs.builder().bucket(ossProperties.getBucket()).build());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public String uploadFileBytes(String bucket, String pathKey, byte[] bytes) {
+        ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
+        return uploadFileStream(bucket, pathKey, stream);
+    }
+
+    @Override
+    public String uploadFileStream(String pathKey, InputStream stream) {
+        return uploadFileStream(ossProperties.getBucket(), pathKey, stream);
+    }
+
+    @Override
+    public String uploadFileStream(String bucket, String pathKey, InputStream stream) {
+
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(stream != null, "stream can't be null");
+        try {
+            String contentType = "application/octet-stream";
+            if (pathKey.contains(".jpg")) {
+                contentType = ("image/jpeg");
+            } else if (pathKey.contains(".png")) {
+                contentType = ("image/png");
+            } else if (pathKey.contains(".json")) {
+                contentType = ("application/json");
+            }
+            PutObjectArgs objectArgs = PutObjectArgs.builder().object(pathKey)
+                    .bucket(bucket)
+                    .contentType(contentType)
+                    .stream(stream, stream.available(), -1).build();
+            ObjectWriteResponse objectWriteResponse = minioClient.putObject(objectArgs);
+            log.info(objectWriteResponse.object());
+
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public File downloadFileTmp(String pathKey) {
+        return downloadFileTmp(ossProperties.getBucket(), pathKey);
+    }
+
+    @Override
+    public File downloadFileTmp(String bucket, String pathKey) {
+        String tmpDir = System.getProperty("java.io.tmpdir");
+        String file = tmpDir + UUID.randomUUID();
+        downloadFile(bucket, pathKey, file);
+        return new File(file);
+    }
+
+    @Override
+    public GetObjectResponse downloadFile(String pathKey, String file) {
+        return downloadFile(ossProperties.getBucket(), pathKey, file);
+    }
+
+    @Override
+    public GetObjectResponse downloadFile(String bucket, String pathKey, String file) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(file != null && file.length() > 0, "file can't be empty");
+        GetObjectArgs objectArgs = GetObjectArgs.builder().object(pathKey)
+                .bucket(bucket).build();
+        log.info("下载开始:下载bucket={},下载pathKey={},下载filePath={}", bucket, pathKey, file);
+        try {
+            return minioClient.getObject(objectArgs);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public GetObjectResponse downloadFile(String pathKey, Consumer<InputStream> handler) {
+        return downloadFile(ossProperties.getBucket(), pathKey, handler);
+    }
+
+    @Override
+    public GetObjectResponse downloadFile(String bucket, String pathKey, Consumer<InputStream> handler) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        InnerUtils.checkArgument(handler != null, "handler can't be null");
+        GetObjectArgs objectArgs = GetObjectArgs.builder().object(pathKey)
+                .bucket(bucket).build();
+        GetObjectResponse ossObject = null;
+        try {
+            ossObject = minioClient.getObject(objectArgs);
+            return ossObject;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public String calculateUrl(String pathKey) {
+        return calculateUrl(ossProperties.getBucket(), pathKey);
+    }
+
+    public String calculateUrl(String bucket, String pathKey) {
+        String host = ossProperties.getHostByBucket(bucket);
+        return host + "/" + bucket + "/" + pathKey;
+    }
+
+    public String calculatePathKey(String url) {
+        String urlPath = InnerUtils.getUrlPath(url);
+        if (urlPath != null && urlPath.startsWith("/")) {
+            String pathKey = urlPath.substring(1);
+            if (pathKey.length() > 0) {
+                return InnerUtils.decodeUrl(pathKey);
+            }
+        }
+        return null;
+    }
+
+    public String calculateHost(String endpoint, String bucket) {
+        return InnerUtils.generateHost(endpoint, bucket);
+    }
+
+
+    @Override
+    public Boolean copyObject(String oldPath, String newPath) {
+        return copyObject(ossProperties.getBucket(), oldPath, ossProperties.getBucket(), newPath);
+    }
+
+    @Override
+    public Boolean copyObject(String bucket, String oldPath, String toBucket, String newPath) {
+        try {
+            ObjectWriteResponse response = minioClient.copyObject(CopyObjectArgs.builder()
+                    .source(CopySource.builder().bucket(bucket).object(oldPath).build())
+                    .bucket(toBucket)
+                    .object(newPath)
+                    .build());
+            if (StrUtil.isNotBlank(response.versionId())) {
+                return true;
+            }
+            return false;
+        } catch (Exception e) {
+            log.error("文件不存在,KeyName{}", e.getMessage());
+            return false;
+        }
+    }
+
+    @Override
+    public void deleteObject(String keyName) {
+        if (doesObjectExist(keyName)) {
+            deleteObject(ossProperties.getBucket(), keyName);
+        } else {
+            log.warn("文件不存在,KeyName{}", keyName);
+        }
+    }
+
+    @Override
+    public void deleteObject(String bucket, String keyName) {
+        try {
+            RemoveObjectArgs objectArgs = RemoveObjectArgs.builder().object(keyName)
+                    .bucket(bucket).build();
+            minioClient.removeObject(objectArgs);
+        } catch (Exception e) {
+            log.info("文件删除失败" + e.getMessage());
+        }
+    }
+
+    @Override
+    public List<String> getFileFolder(String keyName) {
+        return getFileFolder(ossProperties.getBucket(), keyName);
+    }
+
+    @Override
+    public List<String> getFileFolder(String bucket, String keyName) {
+        List<String> list = new ArrayList<>();
+        try {
+            ListObjectsArgs objectArgs = ListObjectsArgs.builder()
+                    .bucket(bucket).recursive(true).prefix(keyName + "/").build();
+            Iterable<Result<Item>> results = minioClient.listObjects(objectArgs);
+            for (Result<Item> result : results) {
+                Item item = result.get();
+                list.add(item.objectName());
+            }
+            log.info("获取文件夹集合={}", JSON.toJSONString(list));
+        } catch (Exception e) {
+            log.error("获取文件夹集合失败={}", e.getMessage());
+        }
+
+        return list;
+    }
+
+    @Override
+    public boolean doesObjectExist(String bucket, String keyName) {
+        GetObjectArgs getArgs = GetObjectArgs.builder()
+                .bucket(bucket)
+                .object(keyName)
+                .build();
+        try {
+            GetObjectResponse response = minioClient.getObject(getArgs);
+            return true;
+        } catch (ErrorResponseException e) {
+            if (e.errorResponse().code().equals("NoSuchKey")) {
+                return false;
+            }
+            log.error("NoSuchKey", e.getMessage());
+        } catch (Exception e) {
+            log.error("连接异常", e.getMessage());
+            return false;
+        }
+        return false;
+    }
+    @Override
+    public String getBucket() {
+        return ossProperties.getBucket();
+    }
+
+    @Override
+    public boolean ossDownloadFileToLocal(String bucket,String keyName, String localPath) {
+        try {
+            minioClient.downloadObject(
+                    DownloadObjectArgs.builder()
+                            .bucket(bucket)
+                            .object(keyName)
+                            .filename(localPath)
+                            .build());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean ossDownloadFileToLocal(String path, String localPath) {
+        return ossDownloadFileToLocal(ossProperties.getBucket(),path,localPath);
+    }
+
+    @Override
+    public boolean doesObjectExist(String keyName) {
+        return doesObjectExist(ossProperties.getBucket(), keyName);
+    }
+
+    @Override
+    public String uploadFileImage(String pathKey, String filePath) {
+        return uploadFileImage(ossProperties.getBucket(), pathKey, filePath);
+    }
+
+    @Override
+    public String uploadFileImage(String bucket, String pathKey, String filePath) {
+        InputStream stream = null;
+        try {
+            String contentType = "application/octet-stream";
+            if (filePath.contains(".jpg")) {
+                contentType = ("image/jpeg");
+            } else if (filePath.contains(".png")) {
+                contentType = ("image/png");
+            } else if (filePath.contains(".json")) {
+                contentType = ("application/json");
+            }
+            stream = FileUtil.getInputStream(new File(filePath));
+            PutObjectArgs objectArgs = PutObjectArgs.builder().object(pathKey)
+                    .bucket(bucket)
+                    .contentType(contentType)
+                    .stream(stream, stream.available(), -1).build();
+            log.info("上传开始:上传bucket={},上传pathKey={},上传filePath={}", bucket, pathKey, filePath);
+            ObjectWriteResponse objectWriteResponse = minioClient.putObject(objectArgs);
+        } catch (Exception e) {
+            return "";
+        } finally {
+            IoUtil.close(stream);
+        }
+        return calculateUrl(bucket, pathKey);
+    }
+
+
+}

+ 91 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/properties/AliyunOssProperties.java

@@ -0,0 +1,91 @@
+package com.fdkankan.filestorage.properties;
+
+
+import com.aliyun.oss.ClientBuilderConfiguration;
+import com.fdkankan.filestorage.InnerUtils;
+import lombok.Data;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * 阿里云OSS配置属性
+ */
+@Data
+@ConfigurationProperties(prefix = "filestorage.oss")
+public class AliyunOssProperties implements Serializable, InitializingBean {
+
+    public static final String PREFIX = "filestorage.oss";
+
+    private String active;
+    /**
+     * 服务地址
+     */
+    private String endpoint;
+
+    /**
+     * 服务地址(内网)
+     */
+    private String internalEndpoint;
+
+    /**
+     * 区域id
+     */
+    private String regionId;
+
+    /**
+     * 访问标识
+     */
+    private String accessKey;
+
+    /**
+     * 访问密钥
+     */
+    private String accessKeySecret;
+
+    /**
+     * 自定义域名, bucket名称忽略大小写
+     */
+    private Map<String, String> bucketCustomDomain;
+
+    /**
+     * Bucket
+     */
+    private String bucket;
+    /**
+     * Client configuration.
+     */
+    private ClientBuilderConfiguration config = new ClientBuilderConfiguration();
+
+    @Override
+    public void afterPropertiesSet() {
+        InnerUtils.checkArgument(!StringUtils.isEmpty(endpoint), "'endpoint' can't be empty");
+        InnerUtils.checkArgument(!StringUtils.isEmpty(bucket), "'bucket' can't be empty");
+    }
+
+    /**
+     * 获取请求到阿里云的服务地址, 内网地址优先
+     */
+    public String getRequestEndpoint() {
+        if (this.internalEndpoint != null && this.internalEndpoint.length() > 0) {
+            return internalEndpoint;
+        }
+        return endpoint;
+    }
+
+    /**
+     * 获取访问访问前缀, 优先级: customDomain > endpoint
+     */
+    public String getHostByBucket(String bucket) {
+        if (bucketCustomDomain != null) {
+            String domain = bucketCustomDomain.get(bucket);
+            if (domain != null && domain.length() > 0) {
+                return domain;
+            }
+        }
+        return InnerUtils.generateHost(this.endpoint, bucket);
+    }
+}

+ 78 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/properties/AwsProperties.java

@@ -0,0 +1,78 @@
+package com.fdkankan.filestorage.properties;
+
+
+import com.fdkankan.filestorage.InnerUtils;
+import lombok.Data;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * MinioOSS配置属性
+ */
+@Data
+@ConfigurationProperties(prefix = "filestorage.awss3")
+public class AwsProperties implements Serializable, InitializingBean {
+
+    public static final String PREFIX = "filestorage.awss3";
+    private String active;
+
+    /**
+     * 服务地址
+     */
+    private String endpoint;
+    /**
+     * 服务地址(内网)
+     */
+    private String internalEndpoint;
+    /**
+     * 访问标识
+     */
+    private String accessKey;
+
+    /**
+     * 访问密钥
+     */
+    private String accessKeySecret;
+
+    /**
+     * 自定义域名, bucket名称忽略大小写
+     */
+    private Map<String, String> bucketCustomDomain;
+    /**
+     * Bucket
+     */
+    private String bucket;
+
+    @Override
+    public void afterPropertiesSet() {
+        InnerUtils.checkArgument(!StringUtils.isEmpty(endpoint), "'endpointUrl' can't be empty");
+        InnerUtils.checkArgument(!StringUtils.isEmpty(bucket), "'bucket' can't be empty");
+    }
+
+    /**
+     * 获取请求到Minio的服务地址, 内网地址优先
+     */
+    public String getRequestEndpoint() {
+        if (this.internalEndpoint != null && this.internalEndpoint.length() > 0) {
+            return internalEndpoint;
+        }
+        return endpoint;
+    }
+
+    /**
+     * 获取访问访问前缀, 优先级: customDomain > endpoint
+     */
+    public String getHostByBucket(String bucket) {
+        if (bucketCustomDomain != null) {
+            String domain = bucketCustomDomain.get(bucket);
+            if (domain != null && domain.length() > 0) {
+                return domain;
+            }
+        }
+        return InnerUtils.generateHost(this.endpoint, bucket);
+    }
+}

+ 92 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/properties/CosProperties.java

@@ -0,0 +1,92 @@
+package com.fdkankan.filestorage.properties;
+
+
+import com.fdkankan.filestorage.InnerUtils;
+import com.qcloud.cos.ClientConfig;
+import lombok.Data;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * 阿里云OSS配置属性
+ */
+@Data
+@ConfigurationProperties(prefix = "filestorage.cos")
+public class CosProperties implements Serializable, InitializingBean {
+
+    public static final String PREFIX = "filestorage.cos";
+    private String active;
+
+
+    /**
+     * 服务地址
+     */
+    private String endpoint;
+
+    /**
+     * 服务地址(内网)
+     */
+    private String internalEndpoint;
+
+    /**
+     * 区域id
+     */
+    private String regionId;
+
+    /**
+     * 访问标识
+     */
+    private String accessKey;
+
+    /**
+     * 访问密钥
+     */
+    private String accessKeySecret;
+
+    /**
+     * 自定义域名, bucket名称忽略大小写
+     */
+    private Map<String, String> bucketCustomDomain;
+
+    /**
+     * Bucket
+     */
+    private String bucket;
+    /**
+     * Client configuration.
+     */
+    private ClientConfig config = new ClientConfig();
+
+    @Override
+    public void afterPropertiesSet() {
+        InnerUtils.checkArgument(!StringUtils.isEmpty(bucket), "'bucket' can't be empty");
+        InnerUtils.checkArgument(!StringUtils.isEmpty(bucket), "'bucket' can't be empty");
+    }
+
+    /**
+     * 获取请求到阿里云的服务地址, 内网地址优先
+     */
+    public String getRequestEndpoint() {
+        if (this.internalEndpoint != null && this.internalEndpoint.length() > 0) {
+            return internalEndpoint;
+        }
+        return endpoint;
+    }
+
+    /**
+     * 获取访问访问前缀, 优先级: customDomain > endpoint
+     */
+    public String getHostByBucket(String bucket) {
+        if (bucketCustomDomain != null) {
+            String domain = bucketCustomDomain.get(bucket);
+            if (domain != null && domain.length() > 0) {
+                return domain;
+            }
+        }
+        return InnerUtils.generateHost(this.endpoint, bucket);
+    }
+}

+ 85 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/properties/MinioProperties.java

@@ -0,0 +1,85 @@
+package com.fdkankan.filestorage.properties;
+
+
+import com.fdkankan.filestorage.InnerUtils;
+import lombok.Data;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * MinioOSS配置属性
+ */
+@Data
+@ConfigurationProperties(prefix = "filestorage.minio")
+public class MinioProperties implements Serializable, InitializingBean {
+
+    public static final String PREFIX = "filestorage.minio";
+    private String active;
+
+    /**
+     * 服务地址
+     */
+    private String endpoint;
+
+    /**
+     * 服务地址(内网)
+     */
+    private String internalEndpoint;
+
+    /**
+     * 区域id
+     */
+    private String regionId;
+
+    /**
+     * 访问标识
+     */
+    private String accessKey;
+
+    /**
+     * 访问密钥
+     */
+    private String accessKeySecret;
+
+    /**
+     * 自定义域名, bucket名称忽略大小写
+     */
+    private Map<String, String> bucketCustomDomain;
+    /**
+     * Bucket
+     */
+    private String bucket;
+
+    @Override
+    public void afterPropertiesSet() {
+        InnerUtils.checkArgument(!StringUtils.isEmpty(endpoint), "'minioUrl' can't be empty");
+        InnerUtils.checkArgument(!StringUtils.isEmpty(bucket), "'bucket' can't be empty");
+    }
+
+    /**
+     * 获取请求到Minio的服务地址, 内网地址优先
+     */
+    public String getRequestEndpoint() {
+        if (this.internalEndpoint != null && this.internalEndpoint.length() > 0) {
+            return internalEndpoint;
+        }
+        return endpoint;
+    }
+
+    /**
+     * 获取访问访问前缀, 优先级: customDomain > endpoint
+     */
+    public String getHostByBucket(String bucket) {
+        if (bucketCustomDomain != null) {
+            String domain = bucketCustomDomain.get(bucket);
+            if (domain != null && domain.length() > 0) {
+                return domain;
+            }
+        }
+        return InnerUtils.generateHost(this.endpoint, bucket);
+    }
+}

+ 1 - 0
4dkankan-utils-filestorage/src/main/resources/META-INF/spring.factories

@@ -0,0 +1 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.fdkankan.filestorage.StorageAutoConfiguration

+ 1 - 0
pom.xml

@@ -29,6 +29,7 @@
         <module>4dkankan-utils-sensitive-word</module>
         <module>4dkankan-utils-sensitive-word</module>
         <module>4dkankan-utils-elasticsearch</module>
         <module>4dkankan-utils-elasticsearch</module>
         <module>4dkankan-utils-reg</module>
         <module>4dkankan-utils-reg</module>
+        <module>4dkankan-utils-filestorage</module>
     </modules>
     </modules>
 
 
     <groupId>com.fdkankan</groupId>
     <groupId>com.fdkankan</groupId>