From 348bd00fa30fbc7eb80db1c0f20a017198498ee6 Mon Sep 17 00:00:00 2001
From: 疯狂的狮子Li <15040126243@163.com>
Date: 星期日, 14 一月 2024 21:20:00 +0800
Subject: [PATCH] [重大更新] 升级 awsS3 到2.X版本 支持异步与自动分片上传下载

---
 ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java |  629 ++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 468 insertions(+), 161 deletions(-)

diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
index 53e05c9..6c402dc 100644
--- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
+++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
@@ -2,73 +2,115 @@
 
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.util.IdUtil;
-import com.amazonaws.ClientConfiguration;
-import com.amazonaws.HttpMethod;
-import com.amazonaws.Protocol;
-import com.amazonaws.auth.AWSCredentials;
-import com.amazonaws.auth.AWSCredentialsProvider;
-import com.amazonaws.auth.AWSStaticCredentialsProvider;
-import com.amazonaws.auth.BasicAWSCredentials;
-import com.amazonaws.client.builder.AwsClientBuilder;
-import com.amazonaws.services.s3.AmazonS3;
-import com.amazonaws.services.s3.AmazonS3Client;
-import com.amazonaws.services.s3.AmazonS3ClientBuilder;
-import com.amazonaws.services.s3.model.*;
+import org.dromara.common.core.constant.Constants;
 import org.dromara.common.core.utils.DateUtils;
 import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.file.FileUtils;
 import org.dromara.common.oss.constant.OssConstant;
 import org.dromara.common.oss.entity.UploadResult;
 import org.dromara.common.oss.enumd.AccessPolicyType;
 import org.dromara.common.oss.enumd.PolicyType;
 import org.dromara.common.oss.exception.OssException;
 import org.dromara.common.oss.properties.OssProperties;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.core.async.AsyncRequestBody;
+import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.s3.S3AsyncClient;
+import software.amazon.awssdk.services.s3.S3Configuration;
+import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
+import software.amazon.awssdk.services.s3.model.S3Exception;
+import software.amazon.awssdk.services.s3.presigner.S3Presigner;
+import software.amazon.awssdk.transfer.s3.S3TransferManager;
+import software.amazon.awssdk.transfer.s3.model.*;
+import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
+import java.net.URI;
 import java.net.URL;
-import java.util.Date;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
 
 /**
  * S3 瀛樺偍鍗忚 鎵�鏈夊吋瀹筍3鍗忚鐨勪簯鍘傚晢鍧囨敮鎸�
  * 闃块噷浜� 鑵捐浜� 涓冪墰浜� minio
  *
- * @author Lion Li
+ * @author AprilWind
  */
 public class OssClient {
 
+    /**
+     * 鏈嶅姟鍟�
+     */
     private final String configKey;
 
+    /**
+     * 閰嶇疆灞炴��
+     */
     private final OssProperties properties;
 
-    private final AmazonS3 client;
+    /**
+     * Amazon S3 寮傛瀹㈡埛绔�
+     */
+    private final S3AsyncClient client;
 
+    /**
+     * 鐢ㄤ簬绠$悊 S3 鏁版嵁浼犺緭鐨勯珮绾у伐鍏�
+     */
+    private final S3TransferManager transferManager;
+
+    /**
+     * AWS S3 棰勭鍚� URL 鐨勭敓鎴愬櫒
+     */
+    private final S3Presigner presigner;
+
+    /**
+     * 鏋勯�犳柟娉�
+     *
+     * @param configKey     閰嶇疆閿�
+     * @param ossProperties Oss閰嶇疆灞炴��
+     */
     public OssClient(String configKey, OssProperties ossProperties) {
         this.configKey = configKey;
         this.properties = ossProperties;
         try {
-            AwsClientBuilder.EndpointConfiguration endpointConfig =
-                new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion());
+            // 鍒涘缓 AWS 璁よ瘉淇℃伅
+            StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
+                AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey()));
 
-            AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey());
-            AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
-            ClientConfiguration clientConfig = new ClientConfiguration();
-            if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) {
-                clientConfig.setProtocol(Protocol.HTTPS);
-            } else {
-                clientConfig.setProtocol(Protocol.HTTP);
-            }
-            AmazonS3ClientBuilder build = AmazonS3Client.builder()
-                .withEndpointConfiguration(endpointConfig)
-                .withClientConfiguration(clientConfig)
-                .withCredentials(credentialsProvider)
-                .disableChunkedEncoding();
-            if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) {
+            //鍒涘缓AWS鍩轰簬 CRT 鐨� S3 瀹㈡埛绔�
+            this.client = S3AsyncClient.crtBuilder()
+                .credentialsProvider(credentialsProvider)
+                .endpointOverride(URI.create(getEndpoint()))
+                .region(of())
+                .targetThroughputInGbps(20.0)
+                .minimumPartSizeInBytes(10 * 1025 * 1024L)
+                .checksumValidationEnabled(false)
+                .build();
+
+            //AWS鍩轰簬 CRT 鐨� S3 AsyncClient 瀹炰緥鐢ㄤ綔 S3 浼犺緭绠$悊鍣ㄧ殑搴曞眰瀹㈡埛绔�
+            this.transferManager = S3TransferManager.builder().s3Client(this.client).build();
+
+            // 妫�鏌ユ槸鍚﹁繛鎺ュ埌 MinIO锛孧inIO 浣跨敤 HTTPS 闄愬埗浣跨敤鍩熷悕璁块棶锛岄渶瑕佸惎鐢ㄨ矾寰勬牱寮忚闂�
+            S3Configuration config = S3Configuration.builder().chunkedEncodingEnabled(false)
                 // minio 浣跨敤https闄愬埗浣跨敤鍩熷悕璁块棶 闇�瑕佹閰嶇疆 绔欑偣濉煙鍚�
-                build.enablePathStyleAccess();
-            }
-            this.client = build.build();
+                .pathStyleAccessEnabled(!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)).build();
 
+            // 鍒涘缓 棰勭鍚� URL 鐨勭敓鎴愬櫒 瀹炰緥锛岀敤浜庣敓鎴� S3 棰勭鍚� URL
+            this.presigner = S3Presigner.builder()
+                .region(of())
+                .credentialsProvider(credentialsProvider)
+                .endpointOverride(URI.create(getDomain()))
+                .serviceConfiguration(config)
+                .build();
+
+            // 鍒涘缓瀛樺偍妗�
             createBucket();
         } catch (Exception e) {
             if (e instanceof OssException) {
@@ -78,126 +120,161 @@
         }
     }
 
+    /**
+     * 鍚屾鍒涘缓瀛樺偍妗�
+     * 濡傛灉瀛樺偍妗朵笉瀛樺湪锛屼細杩涜鍒涘缓锛涘鏋滃瓨鍌ㄦ《瀛樺湪锛屼笉鎵ц浠讳綍鎿嶄綔
+     *
+     * @throws OssException 褰撳垱寤哄瓨鍌ㄦ《鏃跺彂鐢熷紓甯告椂鎶涘嚭
+     */
     public void createBucket() {
+        String bucketName = properties.getBucketName();
         try {
-            String bucketName = properties.getBucketName();
-            if (client.doesBucketExistV2(bucketName)) {
-                return;
+            // 灏濊瘯鑾峰彇瀛樺偍妗剁殑淇℃伅
+            client.headBucket(
+                    x -> x.bucket(bucketName)
+                        .build())
+                .join();
+        } catch (Exception ex) {
+            if (ex.getCause() instanceof NoSuchBucketException) {
+                try {
+                    // 瀛樺偍妗朵笉瀛樺湪锛屽皾璇曞垱寤哄瓨鍌ㄦ《
+                    client.createBucket(
+                            x -> x.bucket(bucketName))
+                        .join();
+
+                    // 璁剧疆瀛樺偍妗剁殑璁块棶绛栫暐锛圔ucket Policy锛�
+                    client.putBucketPolicy(
+                            x -> x.bucket(bucketName)
+                                .policy(getPolicy(bucketName, getAccessPolicy().getPolicyType())))
+                        .join();
+                } catch (S3Exception e) {
+                    // 瀛樺偍妗跺垱寤烘垨绛栫暐璁剧疆澶辫触
+                    throw new OssException("鍒涘缓Bucket澶辫触, 璇锋牳瀵归厤缃俊鎭�:[" + e.getMessage() + "]");
+                }
+            } else {
+                throw new OssException("鍒ゆ柇Bucket鏄惁瀛樺湪澶辫触锛岃鏍稿閰嶇疆淇℃伅:[" + ex.getMessage() + "]");
             }
-            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
-            AccessPolicyType accessPolicy = getAccessPolicy();
-            createBucketRequest.setCannedAcl(accessPolicy.getAcl());
-            client.createBucket(createBucketRequest);
-            client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType()));
-        } catch (Exception e) {
-            throw new OssException("鍒涘缓Bucket澶辫触, 璇锋牳瀵归厤缃俊鎭�:[" + e.getMessage() + "]");
         }
     }
 
-    public UploadResult upload(byte[] data, String path, String contentType) {
-        return upload(new ByteArrayInputStream(data), path, contentType);
+    /**
+     * 涓婁紶鏂囦欢鍒� Amazon S3锛屽苟杩斿洖涓婁紶缁撴灉
+     *
+     * @param filePath  鏈湴鏂囦欢璺緞
+     * @param key       鍦� Amazon S3 涓殑瀵硅薄閿�
+     * @param md5Digest 鏈湴鏂囦欢鐨� MD5 鍝堝笇鍊硷紙鍙�夛級
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult upload(Path filePath, String key, String md5Digest) {
+        try {
+            // 鏋勫缓涓婁紶璇锋眰瀵硅薄
+            FileUpload fileUpload = transferManager.uploadFile(
+                x -> x.putObjectRequest(
+                        y -> y.bucket(properties.getBucketName())
+                            .key(key)
+                            .contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null)
+                            .build())
+                    .addTransferListener(LoggingTransferListener.create())
+                    .source(filePath).build());
+
+            // 绛夊緟涓婁紶瀹屾垚骞惰幏鍙栦笂浼犵粨鏋�
+            CompletedFileUpload uploadResult = fileUpload.completionFuture().join();
+            String eTag = uploadResult.response().eTag();
+
+            // 鎻愬彇涓婁紶缁撴灉涓殑 ETag锛屽苟鏋勫缓涓�涓嚜瀹氫箟鐨� UploadResult 瀵硅薄
+            return UploadResult.builder().url(getUrl() + StringUtils.SLASH + key).filename(key).eTag(eTag).build();
+        } catch (Exception e) {
+            // 鎹曡幏寮傚父骞舵姏鍑鸿嚜瀹氫箟寮傚父
+            throw new OssException("涓婁紶鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
+        } finally {
+            // 鏃犺涓婁紶鏄惁鎴愬姛锛屾渶缁堥兘浼氬垹闄や复鏃舵枃浠�
+            FileUtils.del(filePath);
+        }
     }
 
-    public UploadResult upload(InputStream inputStream, String path, String contentType) {
+    /**
+     * 涓婁紶 InputStream 鍒� Amazon S3
+     *
+     * @param inputStream 瑕佷笂浼犵殑杈撳叆娴�
+     * @param key         鍦� Amazon S3 涓殑瀵硅薄閿�
+     * @param length      杈撳叆娴佺殑闀垮害
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult upload(InputStream inputStream, String key, Long length) {
+        // 濡傛灉杈撳叆娴佷笉鏄� ByteArrayInputStream锛屽垯灏嗗叾璇诲彇涓哄瓧鑺傛暟缁勫啀鍒涘缓 ByteArrayInputStream
         if (!(inputStream instanceof ByteArrayInputStream)) {
             inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
         }
         try {
-            ObjectMetadata metadata = new ObjectMetadata();
-            metadata.setContentType(contentType);
-            metadata.setContentLength(inputStream.available());
-            PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata);
-            // 璁剧疆涓婁紶瀵硅薄鐨� Acl 涓哄叕鍏辫
-            putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
-            client.putObject(putObjectRequest);
+            // 鍒涘缓寮傛璇锋眰浣擄紙length濡傛灉涓虹┖浼氭姤閿欙級
+            BlockingInputStreamAsyncRequestBody body = AsyncRequestBody.forBlockingInputStream(length);
+
+            // 浣跨敤 transferManager 杩涜涓婁紶
+            Upload upload = transferManager.upload(
+                x -> x.requestBody(body)
+                    .putObjectRequest(
+                        y -> y.bucket(properties.getBucketName())
+                            .key(key)
+                            .build())
+                    .build());
+
+            // 灏嗚緭鍏ユ祦鍐欏叆璇锋眰浣�
+            body.writeInputStream(inputStream);
+
+            // 绛夊緟鏂囦欢涓婁紶鎿嶄綔瀹屾垚
+            CompletedUpload uploadResult = upload.completionFuture().join();
+            String eTag = uploadResult.response().eTag();
+
+            // 鎻愬彇涓婁紶缁撴灉涓殑 ETag锛屽苟鏋勫缓涓�涓嚜瀹氫箟鐨� UploadResult 瀵硅薄
+            return UploadResult.builder().url(getUrl() + StringUtils.SLASH + key).filename(key).eTag(eTag).build();
         } catch (Exception e) {
             throw new OssException("涓婁紶鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
         }
-        return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
-    }
-
-    public UploadResult upload(File file, String path) {
-        try {
-            PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, file);
-            // 璁剧疆涓婁紶瀵硅薄鐨� Acl 涓哄叕鍏辫
-            putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
-            client.putObject(putObjectRequest);
-        } catch (Exception e) {
-            throw new OssException("涓婁紶鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
-        }
-        return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
-    }
-
-    public void delete(String path) {
-        path = path.replace(getUrl() + "/", "");
-        try {
-            client.deleteObject(properties.getBucketName(), path);
-        } catch (Exception e) {
-            throw new OssException("鍒犻櫎鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
-        }
-    }
-
-    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
-        return upload(data, getPath(properties.getPrefix(), suffix), contentType);
-    }
-
-    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
-        return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
-    }
-
-    public UploadResult uploadSuffix(File file, String suffix) {
-        return upload(file, getPath(properties.getPrefix(), suffix));
     }
 
     /**
-     * 鑾峰彇鏂囦欢鍏冩暟鎹�
+     * 涓嬭浇鏂囦欢浠� Amazon S3 鍒颁复鏃剁洰褰�
      *
-     * @param path 瀹屾暣鏂囦欢璺緞
+     * @param path 鏂囦欢鍦� Amazon S3 涓殑瀵硅薄閿�
+     * @return 涓嬭浇鍚庣殑鏂囦欢鍦ㄦ湰鍦扮殑涓存椂璺緞
+     * @throws OssException 濡傛灉涓嬭浇澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
      */
-    public ObjectMetadata getObjectMetadata(String path) {
-        path = path.replace(getUrl() + "/", "");
-        S3Object object = client.getObject(properties.getBucketName(), path);
-        return object.getObjectMetadata();
+    public Path fileDownload(String path) {
+        // 浠庤矾寰勪腑绉婚櫎 URL 鍓嶇紑
+        String url = removeBaseUrl(path);
+
+        // 鏋勫缓涓存椂鏂囦欢璺緞 鏂囦欢鍚嶅繀椤绘槸鍞竴涓嶅瓨鍦ㄧ殑锛岃矾寰勫繀椤绘槸瀛樺湪鐨�
+        Path tempFilePath = Paths.get(extractFileName(url));
+        // 浣跨敤 S3TransferManager 涓嬭浇鏂囦欢
+        FileDownload downloadFile = transferManager.downloadFile(
+            x -> x.getObjectRequest(
+                    y -> y.bucket(properties.getBucketName())
+                        .key(url)
+                        .build())
+                .addTransferListener(LoggingTransferListener.create())
+                .destination(tempFilePath)
+                .build());
+        // 绛夊緟鏂囦欢涓嬭浇鎿嶄綔瀹屾垚
+        downloadFile.completionFuture().join();
+        return tempFilePath;
     }
 
-    public InputStream getObjectContent(String path) {
-        path = path.replace(getUrl() + "/", "");
-        S3Object object = client.getObject(properties.getBucketName(), path);
-        return object.getObjectContent();
-    }
-
-    public String getUrl() {
-        String domain = properties.getDomain();
-        String endpoint = properties.getEndpoint();
-        String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://";
-        // 浜戞湇鍔″晢鐩存帴杩斿洖
-        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
-            if (StringUtils.isNotBlank(domain)) {
-                return header + domain;
-            }
-            return header + properties.getBucketName() + "." + endpoint;
+    /**
+     * 鍒犻櫎浜戝瓨鍌ㄦ湇鍔′腑鎸囧畾璺緞涓嬫枃浠�
+     *
+     * @param path 鎸囧畾璺緞
+     */
+    public void delete(String path) {
+        try {
+            client.deleteObject(
+                x -> x.bucket(properties.getBucketName())
+                    .key(removeBaseUrl(path))
+                    .build());
+        } catch (Exception e) {
+            throw new OssException("鍒犻櫎鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
         }
-        // minio 鍗曠嫭澶勭悊
-        if (StringUtils.isNotBlank(domain)) {
-            return header + domain + "/" + properties.getBucketName();
-        }
-        return header + endpoint + "/" + properties.getBucketName();
-    }
-
-    public String getPath(String prefix, String suffix) {
-        // 鐢熸垚uuid
-        String uuid = IdUtil.fastSimpleUUID();
-        // 鏂囦欢璺緞
-        String path = DateUtils.datePath() + "/" + uuid;
-        if (StringUtils.isNotBlank(prefix)) {
-            path = prefix + "/" + path;
-        }
-        return path + suffix;
-    }
-
-
-    public String getConfigKey() {
-        return configKey;
     }
 
     /**
@@ -207,12 +284,197 @@
      * @param second    鎺堟潈鏃堕棿
      */
     public String getPrivateUrl(String objectKey, Integer second) {
-        GeneratePresignedUrlRequest generatePresignedUrlRequest =
-            new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)
-                .withMethod(HttpMethod.GET)
-                .withExpiration(new Date(System.currentTimeMillis() + 1000L * second));
-        URL url = client.generatePresignedUrl(generatePresignedUrlRequest);
+        // 浣跨敤 AWS S3 棰勭鍚� URL 鐨勭敓鎴愬櫒 鑾峰彇瀵硅薄鐨勯绛惧悕 URL
+        URL url = presigner.presignGetObject(
+                x -> x.signatureDuration(Duration.ofSeconds(second))
+                    .getObjectRequest(
+                        y -> y.bucket(properties.getBucketName())
+                            .key(objectKey)
+                            .build())
+                    .build())
+            .url();
         return url.toString();
+    }
+
+    /**
+     * 涓婁紶 byte[] 鏁版嵁鍒� Amazon S3锛屼娇鐢ㄦ寚瀹氱殑鍚庣紑鏋勯�犲璞¢敭銆�
+     *
+     * @param data   瑕佷笂浼犵殑 byte[] 鏁版嵁
+     * @param suffix 瀵硅薄閿殑鍚庣紑
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult uploadSuffix(byte[] data, String suffix) {
+        return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Long.valueOf(data.length));
+    }
+
+    /**
+     * 涓婁紶 InputStream 鍒� Amazon S3锛屼娇鐢ㄦ寚瀹氱殑鍚庣紑鏋勯�犲璞¢敭銆�
+     *
+     * @param inputStream 瑕佷笂浼犵殑杈撳叆娴�
+     * @param suffix      瀵硅薄閿殑鍚庣紑
+     * @param length      杈撳叆娴佺殑闀垮害
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult uploadSuffix(InputStream inputStream, String suffix, Long length) {
+        return upload(inputStream, getPath(properties.getPrefix(), suffix), length);
+    }
+
+    /**
+     * 涓婁紶鏂囦欢鍒� Amazon S3锛屼娇鐢ㄦ寚瀹氱殑鍚庣紑鏋勯�犲璞¢敭
+     *
+     * @param file   瑕佷笂浼犵殑鏂囦欢
+     * @param suffix 瀵硅薄閿殑鍚庣紑
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult uploadSuffix(File file, String suffix) {
+        return upload(file.toPath(), getPath(properties.getPrefix(), suffix), null);
+    }
+
+    /**
+     * 鑾峰彇鏂囦欢杈撳叆娴�
+     *
+     * @param path 瀹屾暣鏂囦欢璺緞
+     * @return 杈撳叆娴�
+     */
+    public InputStream getObjectContent(String path) throws IOException {
+        // 涓嬭浇鏂囦欢鍒颁复鏃剁洰褰�
+        Path tempFilePath = fileDownload(path);
+        // 鍒涘缓杈撳叆娴�
+        InputStream inputStream = Files.newInputStream(tempFilePath);
+        // 鍒犻櫎涓存椂鏂囦欢
+        FileUtils.del(tempFilePath);
+        // 杩斿洖瀵硅薄鍐呭鐨勮緭鍏ユ祦
+        return inputStream;
+    }
+
+    /**
+     * 鑾峰彇 S3 瀹㈡埛绔殑缁堢鐐� URL
+     *
+     * @return 缁堢鐐� URL
+     */
+    public String getEndpoint() {
+        // 鏍规嵁閰嶇疆鏂囦欢涓殑鏄惁浣跨敤 HTTPS锛岃缃崗璁ご閮�
+        String header = getIsHttps();
+        // 鎷兼帴鍗忚澶撮儴鍜岀粓绔偣锛屽緱鍒板畬鏁寸殑缁堢鐐� URL
+        return header + properties.getEndpoint();
+    }
+
+    /**
+     * 鑾峰彇 S3 瀹㈡埛绔殑缁堢鐐� URL锛堣嚜瀹氫箟鍩熷悕锛�
+     *
+     * @return 缁堢鐐� URL
+     */
+    public String getDomain() {
+        // 浠庨厤缃腑鑾峰彇鍩熷悕銆佺粓绔偣銆佹槸鍚︿娇鐢� HTTPS 绛変俊鎭�
+        String domain = properties.getDomain();
+        String endpoint = properties.getEndpoint();
+        String header = getIsHttps();
+
+        // 濡傛灉鏄簯鏈嶅姟鍟嗭紝鐩存帴杩斿洖鍩熷悕鎴栫粓绔偣
+        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
+            return StringUtils.isNotEmpty(domain) ? header + domain : header + endpoint;
+        }
+
+        // 濡傛灉鏄� MinIO锛屽鐞嗗煙鍚嶅苟杩斿洖
+        if (StringUtils.isNotEmpty(domain)) {
+            return domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP) ? domain : header + domain;
+        }
+
+        // 杩斿洖缁堢鐐�
+        return header + endpoint;
+    }
+
+    /**
+     * 鏍规嵁浼犲叆鐨� region 鍙傛暟杩斿洖鐩稿簲鐨� AWS 鍖哄煙
+     * 濡傛灉 region 鍙傛暟闈炵┖锛屼娇鐢� Region.of 鏂规硶鍒涘缓骞惰繑鍥炲搴旂殑 AWS 鍖哄煙瀵硅薄
+     * 濡傛灉 region 鍙傛暟涓虹┖锛岃繑鍥炰竴涓粯璁ょ殑 AWS 鍖哄煙锛堜緥濡傦紝us-east-1锛夛紝浣滀负骞挎硾鏀寔鐨勫尯鍩�
+     *
+     * @return 瀵瑰簲鐨� AWS 鍖哄煙瀵硅薄锛屾垨鑰呴粯璁ょ殑骞挎硾鏀寔鐨勫尯鍩燂紙us-east-1锛�
+     */
+    public Region of() {
+        //AWS 鍖哄煙瀛楃涓�
+        String region = properties.getRegion();
+        // 濡傛灉 region 鍙傛暟闈炵┖锛屼娇鐢� Region.of 鏂规硶鍒涘缓瀵瑰簲鐨� AWS 鍖哄煙瀵硅薄锛屽惁鍒欒繑鍥為粯璁ゅ尯鍩�
+        return StringUtils.isNotEmpty(region) ? Region.of(region) : Region.US_EAST_1;
+    }
+
+    /**
+     * 鑾峰彇浜戝瓨鍌ㄦ湇鍔$殑URL
+     *
+     * @return 鏂囦欢璺緞
+     */
+    public String getUrl() {
+        String domain = properties.getDomain();
+        String endpoint = properties.getEndpoint();
+        String header = getIsHttps();
+        // 浜戞湇鍔″晢鐩存帴杩斿洖
+        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
+            return header + (StringUtils.isNotEmpty(domain) ? domain : properties.getBucketName() + "." + endpoint);
+        }
+        // MinIO 鍗曠嫭澶勭悊
+        if (StringUtils.isNotEmpty(domain)) {
+            // 濡傛灉 domain 浠� "https://" 鎴� "http://" 寮�澶�
+            return (domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP)) ?
+                domain + StringUtils.SLASH + properties.getBucketName() : header + domain + StringUtils.SLASH + properties.getBucketName();
+        }
+        return header + endpoint + StringUtils.SLASH + properties.getBucketName();
+    }
+
+    /**
+     * 鐢熸垚涓�涓鍚堢壒瀹氳鍒欑殑銆佸敮涓�鐨勬枃浠惰矾寰勩�傞�氳繃浣跨敤鏃ユ湡銆乁UID銆佸墠缂�鍜屽悗缂�绛夊厓绱犵殑缁勫悎锛岀‘淇濅簡鏂囦欢璺緞鐨勭嫭涓�鏃犱簩鎬�
+     *
+     * @param prefix 鍓嶇紑
+     * @param suffix 鍚庣紑
+     * @return 鏂囦欢璺緞
+     */
+    public String getPath(String prefix, String suffix) {
+        // 鐢熸垚uuid
+        String uuid = IdUtil.fastSimpleUUID();
+        // 鐢熸垚鏃ユ湡璺緞
+        String datePath = DateUtils.datePath();
+        // 鎷兼帴璺緞
+        String path = StringUtils.isNotEmpty(prefix) ?
+            prefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid : datePath + StringUtils.SLASH + uuid;
+        return path + suffix;
+    }
+
+    /**
+     * 绉婚櫎璺緞涓殑鍩虹URL閮ㄥ垎锛屽緱鍒扮浉瀵硅矾寰�
+     *
+     * @param path 瀹屾暣鐨勮矾寰勶紝鍖呮嫭鍩虹URL鍜岀浉瀵硅矾寰�
+     * @return 鍘婚櫎鍩虹URL鍚庣殑鐩稿璺緞
+     */
+    public String removeBaseUrl(String path) {
+        return path.replace(getUrl() + StringUtils.SLASH, "");
+    }
+
+    /**
+     * 浠庢枃浠惰矾寰勪腑鎻愬彇鏂囦欢鍚�
+     *
+     * @param path 鏂囦欢璺緞
+     * @return 鎻愬彇鐨勬枃浠跺悕鎴栭粯璁ゆ枃浠跺悕
+     */
+    public String extractFileName(String path) {
+        return FileUtils.getTmpDir() + StringUtils.SLASH + Paths.get(path).getFileName().toString();
+    }
+
+    /**
+     * 鏈嶅姟鍟�
+     */
+    public String getConfigKey() {
+        return configKey;
+    }
+
+    /**
+     * 鑾峰彇鏄惁浣跨敤 HTTPS 鐨勯厤缃紝骞惰繑鍥炵浉搴旂殑鍗忚澶撮儴銆�
+     *
+     * @return 鍗忚澶撮儴锛屾牴鎹槸鍚︿娇鐢� HTTPS 杩斿洖 "https://" 鎴� "http://"
+     */
+    public String getIsHttps() {
+        return OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? Constants.HTTPS : Constants.HTTP;
     }
 
     /**
@@ -231,32 +493,77 @@
         return AccessPolicyType.getByType(properties.getAccessPolicy());
     }
 
+    /**
+     * 鐢熸垚 AWS S3 瀛樺偍妗惰闂瓥鐣�
+     *
+     * @param bucketName 瀛樺偍妗�
+     * @param policyType 妗剁瓥鐣ョ被鍨�
+     * @return 绗﹀悎 AWS S3 瀛樺偍妗惰闂瓥鐣ユ牸寮忕殑瀛楃涓�
+     */
     private static String getPolicy(String bucketName, PolicyType policyType) {
-        StringBuilder builder = new StringBuilder();
-        builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n");
-        builder.append(switch (policyType) {
-            case WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n";
-            case READ_WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n";
-            default -> "\"s3:GetBucketLocation\"\n";
-        });
-        builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
-        builder.append(bucketName);
-        builder.append("\"\n},\n");
-        if (policyType == PolicyType.READ) {
-            builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
-            builder.append(bucketName);
-            builder.append("\"\n},\n");
-        }
-        builder.append("{\n\"Action\": ");
-        builder.append(switch (policyType) {
-            case WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n";
-            case READ_WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n";
-            default -> "\"s3:GetObject\",\n";
-        });
-        builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
-        builder.append(bucketName);
-        builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n");
-        return builder.toString();
+        String policy = switch (policyType) {
+            case WRITE -> """
+                {
+                  "Version": "2012-10-17",
+                  "Statement": []
+                }
+                """;
+            case READ_WRITE -> """
+                {
+                  "Version": "2012-10-17",
+                  "Statement": [
+                    {
+                      "Effect": "Allow",
+                      "Principal": "*",
+                      "Action": [
+                        "s3:GetBucketLocation",
+                        "s3:ListBucket",
+                        "s3:ListBucketMultipartUploads"
+                      ],
+                      "Resource": "arn:aws:s3:::bucketName"
+                    },
+                    {
+                      "Effect": "Allow",
+                      "Principal": "*",
+                      "Action": [
+                        "s3:AbortMultipartUpload",
+                        "s3:DeleteObject",
+                        "s3:GetObject",
+                        "s3:ListMultipartUploadParts",
+                        "s3:PutObject"
+                      ],
+                      "Resource": "arn:aws:s3:::bucketName/*"
+                    }
+                  ]
+                }
+                """;
+            case READ -> """
+                {
+                  "Version": "2012-10-17",
+                  "Statement": [
+                    {
+                      "Effect": "Allow",
+                      "Principal": "*",
+                      "Action": ["s3:GetBucketLocation"],
+                      "Resource": "arn:aws:s3:::bucketName"
+                    },
+                    {
+                      "Effect": "Deny",
+                      "Principal": "*",
+                      "Action": ["s3:ListBucket"],
+                      "Resource": "arn:aws:s3:::bucketName"
+                    },
+                    {
+                      "Effect": "Allow",
+                      "Principal": "*",
+                      "Action": "s3:GetObject",
+                      "Resource": "arn:aws:s3:::bucketName/*"
+                    }
+                  ]
+                }
+                """;
+        };
+        return policy.replaceAll("bucketName", bucketName);
     }
 
 }

--
Gitblit v1.9.3