xiewj hai 1 día
pai
achega
c4147ec563

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

@@ -17,6 +17,9 @@
         <aws.version>1.12.481</aws.version>
         <minio.version>8.5.6</minio.version>
         <aliyun-sdk-oss.version>3.15.1</aliyun-sdk-oss.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
 
     </properties>
 

+ 87 - 36
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/StorageAutoConfiguration.java

@@ -11,8 +11,10 @@ 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.s3.S3Template;
 import com.fdkankan.filestorage.properties.*;
 import com.qcloud.cos.COSClient;
 import com.qcloud.cos.ClientConfig;
@@ -33,61 +35,61 @@ import org.springframework.context.annotation.Configuration;
  * @date 2023/8/28
  */
 @Configuration
-@ConditionalOnProperty(prefix  = "filestorage",name= "active")
+@ConditionalOnProperty(prefix = "filestorage", name = "active")
 @Slf4j
 public class StorageAutoConfiguration {
 
     @Bean
-    @ConditionalOnProperty(prefix  = "filestorage",name= "active")
-    FileStroageProperties fileStroageProperties(){
+    @ConditionalOnProperty(prefix = "filestorage", name = "active")
+    FileStroageProperties fileStroageProperties() {
         return new FileStroageProperties();
     }
 
     @Bean
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "oss")
-    public AliyunOssProperties aliyunOssProperties(FileStroageProperties fileStroageProperties){
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "oss")
+    public AliyunOssProperties aliyunOssProperties(FileStroageProperties fileStroageProperties) {
         AliyunOssProperties aliyunOssProperties = new AliyunOssProperties();
-        aliyunOssProperties .setActive(fileStroageProperties.getActive());
+        aliyunOssProperties.setActive(fileStroageProperties.getActive());
         return aliyunOssProperties;
     }
 
     @Bean(name = "aliyunOssClient")
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "oss")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "oss")
     public OSS aliyunOssClient(AliyunOssProperties properties) {
         return new OSSClientBuilder().build(properties.getRequestEndpoint(),
                 properties.getAccessKey(),
                 properties.getAccessKeySecret(),
                 properties.getConfig());
     }
+
     @Bean(name = "aliyunOssTemplate")
     @Qualifier("oss")
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "oss")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "oss")
     public FileStorageTemplate aliyunOssTemplate(OSS oss, AliyunOssProperties properties) {
-        return new  AliyunOssTemplate(oss, properties);
+        return new AliyunOssTemplate(oss, properties);
     }
 
-
     @Bean
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "aws")
-    public AwsProperties awsProperties(FileStroageProperties fileStroageProperties){
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "aws")
+    public AwsProperties awsProperties(FileStroageProperties fileStroageProperties) {
         AwsProperties awsProperties = new AwsProperties();
         awsProperties.setActive(fileStroageProperties.getActive());
         return awsProperties;
     }
 
-
     @Bean(name = "amazonS3Client")
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "aws")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "aws")
     public AmazonS3 amazonS3Client(AwsProperties properties) {
-        BasicAWSCredentials awsCreds = new BasicAWSCredentials(properties.getAccessKey(), properties.getAccessKeySecret());
+        BasicAWSCredentials awsCreds = new BasicAWSCredentials(properties.getAccessKey(),
+                properties.getAccessKeySecret());
         ClientConfiguration clientConfiguration = new ClientConfiguration();
         clientConfiguration.setMaxErrorRetry(5);
         clientConfiguration.setMaxConnections(300);
         clientConfiguration.setConnectionTimeout(5000);
         clientConfiguration.setSocketTimeout(5000);
         Regions regions = Regions.EU_WEST_2;
-        if (ObjectUtil.isNotEmpty(properties.getRegion())){
-            regions=Regions.fromName(properties.getRegion());
+        if (ObjectUtil.isNotEmpty(properties.getRegion())) {
+            regions = Regions.fromName(properties.getRegion());
         }
         AmazonS3 s3 = AmazonS3ClientBuilder.standard()
                 .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
@@ -97,34 +99,33 @@ public class StorageAutoConfiguration {
         return s3;
     }
 
-
-
     @Bean(name = "AwsOssTemplate")
     @Qualifier("aws")
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "aws")
-    public FileStorageTemplate AwsOssTemplate(AmazonS3 oss, AwsProperties properties) {
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "aws")
+    public FileStorageTemplate AwsOssTemplate(@Qualifier("amazonS3Client") AmazonS3 oss, AwsProperties properties) {
         return new AwsTemplate(oss, properties);
     }
 
-
     @Bean
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "cos")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "cos")
     public CosProperties cosProperties(FileStroageProperties fileStroageProperties) {
         CosProperties cosProperties = new CosProperties();
         cosProperties.setActive(fileStroageProperties.getActive());
         return cosProperties;
     }
 
-
     @Bean(name = "cosClient")
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "cos")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "cos")
     public COSClient cosClient(CosProperties properties) {
 
         // 1 初始化用户身份信息(secretId, secretKey)。
-        // SECRETID 和 SECRETKEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理
+        // 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 部分。
+        // 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);
@@ -137,25 +138,23 @@ public class StorageAutoConfiguration {
         return cosClient;
     }
 
-
-
     @Bean(name = "cosTemplate")
     @Qualifier("cos")
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "cos")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "cos")
     public FileStorageTemplate cosTemplate(COSClient ossClient, CosProperties cosProperties) {
         return new CosTemplate(ossClient, cosProperties);
     }
 
-
     @Bean
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "minio")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "minio")
     public MinioProperties minioProperties(FileStroageProperties fileStroageProperties) {
         MinioProperties minioProperties = new MinioProperties();
         minioProperties.setActive(fileStroageProperties.getActive());
         return minioProperties;
     }
+
     @Bean(name = "minioClient")
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "minio")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "minio")
     public MinioClient minioClient(MinioProperties minioProperties) {
         return MinioClient.builder()
                 .endpoint(minioProperties.getEndpoint())
@@ -163,12 +162,64 @@ public class StorageAutoConfiguration {
                 .build();
     }
 
-
     @Bean(name = "minioOssTemplate")
     @Qualifier("minio")
-    @ConditionalOnProperty(name = "filestorage.active",havingValue = "minio")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "minio")
     public FileStorageTemplate minioOssTemplate(MinioClient oss, MinioProperties properties) {
         return new MinioTemplate(oss, properties);
     }
 
+    @Bean
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "s3")
+    public S3Properties s3Properties(FileStroageProperties fileStroageProperties) {
+        S3Properties s3Properties = new S3Properties();
+        s3Properties.setActive(fileStroageProperties.getActive());
+        return s3Properties;
+    }
+
+    @Bean(name = "s3Client")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "s3")
+    public AmazonS3 s3Client(S3Properties properties) {
+        BasicAWSCredentials awsCreds = new BasicAWSCredentials(properties.getAccessKey(),
+                properties.getAccessKeySecret());
+        ClientConfiguration clientConfiguration = new ClientConfiguration();
+        clientConfiguration.setMaxErrorRetry(5);
+        clientConfiguration.setMaxConnections(300);
+        clientConfiguration.setConnectionTimeout(5000);
+        clientConfiguration.setSocketTimeout(5000);
+
+        AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard()
+                .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
+                .withClientConfiguration(clientConfiguration);
+
+        // 设置Endpoint和Region
+        if (ObjectUtil.isNotEmpty(properties.getEndpoint())) {
+            // 如果指定了Endpoint,通常也建议开启PathStyleAccess (MinIO/RustFS等兼容S3的服务通常需要)
+            builder.withEndpointConfiguration(new AmazonS3ClientBuilder.EndpointConfiguration(
+                    properties.getEndpoint(),
+                    properties.getRegion() != null ? properties.getRegion() : Regions.DEFAULT_REGION.getName()));
+            // S3兼容服务(如MinIO, RustFS)通常需要开启 path-style access
+            // 默认 flase (Virtual-Hosted-Style), 设置为 true 即使用 Path-Style
+            if (properties.isPathStyleAccessEnabled()) {
+                builder.withPathStyleAccessEnabled(true);
+            }
+        } else {
+            // 如果没有Endpoint,尝试使用Region
+            Regions regions = Regions.DEFAULT_REGION;
+            if (ObjectUtil.isNotEmpty(properties.getRegion())) {
+                regions = Regions.fromName(properties.getRegion());
+            }
+            builder.withRegion(regions);
+        }
+
+        return builder.build();
+    }
+
+    @Bean(name = "s3Template")
+    @Qualifier("s3")
+    @ConditionalOnProperty(name = "filestorage.active", havingValue = "s3")
+    public FileStorageTemplate s3Template(@Qualifier("s3Client") AmazonS3 s3Client, S3Properties properties) {
+        return new S3Template(s3Client, properties);
+    }
+
 }

+ 95 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/properties/S3Properties.java

@@ -0,0 +1,95 @@
+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;
+
+/**
+ * 通用S3配置属性
+ * filestorage:
+ *   active: s3
+ *   s3:
+ *     endpoint: http://rustfs-endpoint.example.com
+ *     access-key: your-access-key
+ *     access-key-secret: your-secret-key
+ *     bucket: your-bucket-name
+ *     path-style-access-enabled: true # Recommended for RustFS/MinIO
+ */
+@Data
+@ConfigurationProperties(prefix = "filestorage.s3")
+public class S3Properties implements Serializable, InitializingBean {
+
+    public static final String PREFIX = "filestorage.s3";
+    
+    private String active;
+
+    /**
+     * 服务地址 (Endpoint)
+     */
+    private String endpoint;
+    /**
+     * 服务地址(内网)
+     */
+    private String internalEndpoint;
+
+    /**
+     * 访问标识 (Access Key)
+     */
+    private String accessKey;
+
+    /**
+     * 访问密钥 (Secret Key)
+     */
+    private String accessKeySecret;
+
+    /**
+     * 是否开启 PathStyleAccess (MinIO/RustFS 通常需要开启)
+     */
+    private boolean pathStyleAccessEnabled = true;
+
+    /**
+     * 自定义域名, bucket名称忽略大小写
+     */
+    private Map<String, String> bucketCustomDomain;
+
+    /**
+     * Bucket名称
+     */
+    private String bucket;
+
+    /**
+     * Region (部分S3兼容服务需要指定)
+     */
+    private String region;
+
+    /**
+     * return fullPath
+     */
+    private boolean fullPath = true;
+
+    @Override
+    public void afterPropertiesSet() {
+        InnerUtils.checkArgument(!StringUtils.isEmpty(endpoint), "'endpoint' can't be empty");
+        InnerUtils.checkArgument(!StringUtils.isEmpty(bucket), "'bucket' can't be empty");
+        InnerUtils.checkArgument(!StringUtils.isEmpty(accessKey), "'accessKey' can't be empty");
+        InnerUtils.checkArgument(!StringUtils.isEmpty(accessKeySecret), "'accessKeySecret' can't be empty");
+    }
+
+    /**
+     * 获取访问访问前缀, 优先级: 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);
+    }
+}

+ 554 - 0
4dkankan-utils-filestorage/src/main/java/com/fdkankan/filestorage/s3/S3Template.java

@@ -0,0 +1,554 @@
+package com.fdkankan.filestorage.s3;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.amazonaws.AmazonServiceException;
+import com.amazonaws.HttpMethod;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.model.*;
+import com.fdkankan.filestorage.Consumer;
+import com.fdkankan.filestorage.FileStorageTemplate;
+import com.fdkankan.filestorage.InnerUtils;
+import com.fdkankan.filestorage.properties.S3Properties;
+import lombok.Getter;
+import lombok.SneakyThrows;
+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.ObjectUtils;
+
+import java.io.*;
+import java.net.URI;
+import java.net.URL;
+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;
+
+/**
+ * 通用S3操作模版类
+ */
+@Getter
+@Slf4j
+@Service("S3Template")
+@ConditionalOnProperty(prefix = S3Properties.PREFIX, name = "filestorage.active", havingValue = "s3")
+public class S3Template implements FileStorageTemplate {
+
+    private final AmazonS3 amazonS3Client;
+    private final S3Properties s3Properties;
+
+    @Override
+    public String getActive() {
+        return this.s3Properties.getActive();
+    }
+
+    public S3Template(AmazonS3 amazonS3Client, S3Properties s3Properties) {
+        this.amazonS3Client = amazonS3Client;
+        this.s3Properties = s3Properties;
+    }
+
+    @Override
+    public String uploadFile(String pathKey, String filePath) {
+        return uploadFile(s3Properties.getBucket(), pathKey, filePath);
+    }
+
+    @Override
+    public String uploadFile(String pathKey, String filePath, Map<String, String> headers) {
+        return uploadFile(s3Properties.getBucket(), pathKey, filePath, headers);
+    }
+
+    @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 uploadFile(String bucket, String pathKey, String filePath, Map<String, String> headers) {
+        InnerUtils.checkArgument(bucket != null && bucket.length() > 0, "bucket can't be empty");
+        InnerUtils.checkArgument(pathKey != null && pathKey.length() > 0, "pathKey can't be empty");
+        BufferedInputStream stream = null;
+        try {
+            ObjectMetadata metadata = new ObjectMetadata();
+            if (ObjectUtil.isNotEmpty(headers)) {
+                for (Map.Entry<String, String> header : headers.entrySet()) {
+                    metadata.setHeader(header.getKey(), header.getValue());
+                }
+            }
+            stream = FileUtil.getInputStream(new File(filePath));
+            metadata.setContentLength(stream.available());
+            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);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public String uploadFileText(String pathKey, String content) {
+        return uploadFileText(s3Properties.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(s3Properties.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(s3Properties.getBucket(), pathKey, bytes);
+    }
+
+    public void createbucket() {
+        try {
+            if (StrUtil.isEmpty(s3Properties.getBucket())) {
+                throw new IllegalArgumentException("桶名称不能为空!");
+            }
+            try {
+                if (!amazonS3Client.doesBucketExistV2(s3Properties.getBucket())) {
+                    amazonS3Client.createBucket(s3Properties.getBucket());
+                }
+            } catch (Exception 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(s3Properties.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");
+
+            }
+            metadata.setContentLength(stream.available());
+            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);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+        return calculateUrl(bucket, pathKey);
+    }
+
+    @Override
+    public File downloadFileTmp(String pathKey) {
+        return downloadFileTmp(s3Properties.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(s3Properties.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 {
+            File downloadFile = new File(file);
+            if (!FileUtil.exist(downloadFile.getParent())) {
+                FileUtil.mkdir(downloadFile.getParent());
+            }
+            return amazonS3Client.getObject(request, downloadFile);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public ObjectMetadata downloadFile(String pathKey, Consumer<InputStream> handler) {
+        return downloadFile(s3Properties.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 {
+                    object.close();
+                    content.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+        return object.getObjectMetadata();
+    }
+
+    public String calculateUrl(String pathKey) {
+        return calculateUrl(s3Properties.getBucket(), pathKey);
+    }
+
+    public String calculateUrl(String bucket, String pathKey) {
+        String host = s3Properties.getHostByBucket(bucket);
+        return s3Properties.isFullPath() ? host + "/" + pathKey : pathKey;
+    }
+
+    @Override
+    public String getHostByBucket(String bucket) {
+        return s3Properties.getHostByBucket(bucket);
+    }
+
+    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(s3Properties.getBucket(), oldPath, s3Properties.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 -> {
+                amazonS3Client.copyObject(bucket, key, toBucket, key.replace(oldPath, newPath));
+            });
+            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(s3Properties.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(s3Properties.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);
+        } catch (Exception e) {
+            log.error("获取文件夹集合失败={}", e.getMessage());
+        }
+
+        return list;
+    }
+
+    @Override
+    public boolean doesObjectExist(String bucket, String keyName) {
+        try {
+            if ("/".equals(keyName.substring(0, 1))) {
+                keyName = keyName.substring(1);
+            }
+            return amazonS3Client.doesObjectExist(bucket, keyName);
+        } 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 s3Properties.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("s3下载文件失败,key=" + amazonS3Client, e);
+        } finally {
+        }
+        return false;
+    }
+
+    @Override
+    public boolean ossDownloadFileToLocal(String path, String localPath) {
+        return ossDownloadFileToLocal(s3Properties.getBucket(), path, localPath);
+    }
+
+    @Override
+    public boolean doesObjectExist(String keyName) {
+        return doesObjectExist(s3Properties.getBucket(), keyName);
+    }
+
+    @Override
+    public String uploadFileImage(String pathKey, String filePath) {
+        return uploadFileImage(s3Properties.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));
+            metadata.setContentLength(stream.available());
+            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);
+    }
+
+    @Override
+    public String getFileContent(String bucket, String keyName) throws Exception {
+        try (S3Object object = amazonS3Client.getObject(new GetObjectRequest(bucket, keyName));
+                S3ObjectInputStream inputStream = object.getObjectContent();
+                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+            StringBuilder content = new StringBuilder();
+            while (true) {
+                String line = reader.readLine();
+                if (line == null)
+                    break;
+                content.append(line);
+            }
+            return content.toString();
+        } catch (Exception e) {
+            throw e;
+        }
+    }
+
+    @Override
+    public String getFileContent(String keyName) throws Exception {
+        return this.getFileContent(this.getBucket(), keyName);
+    }
+
+    @Override
+    public Long getSpace(String bucket, String key) {
+        List<String> keyList = new ArrayList<>();
+        Long total = 0L;
+        boolean flag = true;
+        String nextMaker = null;
+        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
+        listObjectsRequest.setBucketName(bucket);
+        listObjectsRequest.setPrefix(key);
+        listObjectsRequest.setMaxKeys(200);
+
+        do {
+            listObjectsRequest.setMarker(nextMaker);
+            ObjectListing objectListing = amazonS3Client.listObjects(listObjectsRequest);
+            List<S3ObjectSummary> objectSummaries = objectListing.getObjectSummaries();
+            Long space = objectSummaries.stream().mapToLong(S3ObjectSummary::getSize).sum();
+            total += space;
+            nextMaker = objectListing.getNextMarker();
+            flag = objectListing.isTruncated();
+        } while (flag);
+
+        return total;
+    }
+
+    @Override
+    public Long getSpace(String key) {
+        return getSpace(s3Properties.getBucket(), key);
+    }
+
+    @SneakyThrows
+    @Override
+    public String getInternalEndpoint(String bucket, String key) {
+        return s3Properties.getInternalEndpoint()+"/"+bucket+"/"+key;
+
+    }
+
+    @Override
+    @SneakyThrows
+    public String getInternalEndpoint(String key) {
+        return getInternalEndpoint(s3Properties.getBucket(), key);
+    }
+
+    @Override
+    public void copyFolder(String sourcePath, String targetPath) {
+        copyFolder(s3Properties.getBucket(), sourcePath, s3Properties.getBucket(), targetPath);
+    }
+
+    @Override
+    public void copyFolder(String sourceBucketName, String sourcePath, String targetBucketName, String targetPath) {
+        try {
+            List<String> files = this.getFileFolder(sourceBucketName, sourcePath);
+            if (ObjectUtils.isEmpty(files)) {
+                return;
+            }
+            files.stream().forEach(file -> {
+                this.copyObject(sourceBucketName, file, targetBucketName, file.replace(sourcePath, targetPath));
+            });
+        } catch (Exception e) {
+            log.error("列举文件目录失败,key:" + sourcePath, e);
+        }
+    }
+
+    @Override
+    public URL getPresignedUrl(String bucket, String key) {
+        java.util.Date expiration = new java.util.Date();
+        long expTimeMillis = expiration.getTime();
+        expTimeMillis += 1000 * 60 * 60 * 8;
+        expiration.setTime(expTimeMillis);
+        GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket, key)
+                .withMethod(HttpMethod.PUT).withExpiration(expiration);
+        return amazonS3Client.generatePresignedUrl(generatePresignedUrlRequest);
+    }
+
+    @Override
+    public URL getPresignedUrl(String key) {
+        return getPresignedUrl(s3Properties.getBucket(), key);
+    }
+}