From 9dc5708d045c564667cdc4d5fda4779a3f1e3e06 Mon Sep 17 00:00:00 2001
From: Jerry Yan <792602257@qq.com>
Date: Mon, 24 Feb 2025 18:31:37 +0800
Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81s3=20storage?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 12 ++
.../ycwl/basic/storage/StorageFactory.java | 3 +
.../basic/storage/adapters/AwsOssAdapter.java | 196 ++++++++++++++++++
.../storage/entity/AwsOssStorageConfig.java | 35 ++++
.../ycwl/basic/storage/enums/StorageType.java | 1 +
src/main/resources/application-dev.yml | 10 +
src/main/resources/application-prod.yml | 10 +
7 files changed, 267 insertions(+)
create mode 100644 src/main/java/com/ycwl/basic/storage/adapters/AwsOssAdapter.java
create mode 100644 src/main/java/com/ycwl/basic/storage/entity/AwsOssStorageConfig.java
diff --git a/pom.xml b/pom.xml
index 1fbb567..2c6411e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -175,6 +175,18 @@
3.17.4
+
+
+ com.amazonaws
+ aws-java-sdk-core
+ 1.11.24
+
+
+ com.amazonaws
+ aws-java-sdk-s3
+ 1.11.24
+
+
com.aliyun
diff --git a/src/main/java/com/ycwl/basic/storage/StorageFactory.java b/src/main/java/com/ycwl/basic/storage/StorageFactory.java
index 34b83c0..5ece8d6 100644
--- a/src/main/java/com/ycwl/basic/storage/StorageFactory.java
+++ b/src/main/java/com/ycwl/basic/storage/StorageFactory.java
@@ -1,6 +1,7 @@
package com.ycwl.basic.storage;
import com.ycwl.basic.storage.adapters.AliOssAdapter;
+import com.ycwl.basic.storage.adapters.AwsOssAdapter;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.storage.adapters.LocalStorageAdapter;
import com.ycwl.basic.storage.entity.StorageConfig;
@@ -23,6 +24,8 @@ public class StorageFactory {
switch (storageType) {
case LOCAL:
return new LocalStorageAdapter();
+ case AWS_OSS:
+ return new AwsOssAdapter();
case ALI_OSS:
return new AliOssAdapter();
default:
diff --git a/src/main/java/com/ycwl/basic/storage/adapters/AwsOssAdapter.java b/src/main/java/com/ycwl/basic/storage/adapters/AwsOssAdapter.java
new file mode 100644
index 0000000..7cbbc3b
--- /dev/null
+++ b/src/main/java/com/ycwl/basic/storage/adapters/AwsOssAdapter.java
@@ -0,0 +1,196 @@
+package com.ycwl.basic.storage.adapters;
+
+import com.amazonaws.ClientConfiguration;
+import com.amazonaws.HttpMethod;
+import com.amazonaws.Protocol;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3Client;
+import com.amazonaws.services.s3.AmazonS3ClientBuilder;
+import com.amazonaws.services.s3.S3ClientOptions;
+import com.amazonaws.services.s3.model.*;
+import com.ycwl.basic.storage.entity.AwsOssStorageConfig;
+import com.ycwl.basic.storage.entity.StorageConfig;
+import com.ycwl.basic.storage.entity.StorageFileObject;
+import com.ycwl.basic.storage.exceptions.StorageConfigException;
+import com.ycwl.basic.storage.exceptions.StorageException;
+import com.ycwl.basic.storage.exceptions.UploadFileFailedException;
+import com.ycwl.basic.storage.utils.StorageUtil;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class AwsOssAdapter extends AStorageAdapter {
+ private AwsOssStorageConfig config;
+
+ @Override
+ public void loadConfig(Map _config) {
+ AwsOssStorageConfig config = new AwsOssStorageConfig();
+ config.setAccessKeyId(_config.get("accessKeyId"));
+ config.setAccessKeySecret(_config.get("accessKeySecret"));
+ config.setBucketName(_config.get("bucketName"));
+ config.setEndpoint(_config.get("endpoint"));
+ config.setRegion(_config.get("region"));
+ config.setUrl(_config.get("url"));
+ config.setPrefix(_config.get("prefix"));
+ config.checkEverythingOK();
+ this.config = config;
+ }
+
+ @Override
+ public void setConfig(StorageConfig config) {
+ if (config == null) {
+ throw new StorageConfigException("配置为空");
+ }
+ if (config instanceof AwsOssStorageConfig) {
+ this.config = (AwsOssStorageConfig) config;
+ } else {
+ throw new StorageConfigException("配置类型错误,传入的类为:" + config.getClass().getName());
+ }
+ }
+
+ @Override
+ public String uploadFile(InputStream inputStream, String... path) {
+ if (inputStream == null) {
+ return null;
+ }
+ String fullPath = buildPath(path);
+ AmazonS3 s3Client = getS3Client();
+ try {
+ ObjectMetadata metadata = new ObjectMetadata();
+ s3Client.putObject(new PutObjectRequest(config.getBucketName(), fullPath, inputStream, metadata));
+ return getUrl(path);
+ } catch (Exception e) {
+ throw new UploadFileFailedException("上传文件失败:" + e.getMessage());
+ }
+ }
+
+ @Override
+ public boolean deleteFile(String... path) {
+ AmazonS3 s3Client = getS3Client();
+ try {
+ s3Client.deleteObject(config.getBucketName(), buildPath(path));
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String getUrl(String... path) {
+ return config.getUrl() + "/" + buildPath(path);
+ }
+
+ @Override
+ public String getUrlForDownload(Date expireDate, String... path) {
+ AmazonS3 s3Client = getS3Client();
+ URL url = s3Client.generatePresignedUrl(config.getBucketName(), buildPath(path), expireDate);
+ return url.toString();
+ }
+
+ @Override
+ public String getUrlForUpload(Date expireDate, String contentType, String... path) {
+ AmazonS3 s3Client = getS3Client();
+ GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(config.getBucketName(), buildPath(path));
+ request.setMethod(HttpMethod.PUT);
+ if (StringUtils.isNotBlank(contentType)) {
+ request.setContentType(contentType);
+ }
+ request.setExpiration(expireDate);
+ URL url = s3Client.generatePresignedUrl(request);
+ return url.toString();
+ }
+
+ @Override
+ public List listDir(String... path) {
+ AmazonS3 s3Client = getS3Client();
+ ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request()
+ .withBucketName(config.getBucketName())
+ .withPrefix(buildPath(path) + "/")
+ .withMaxKeys(1000);
+ boolean isTruncated = true;
+ String continuationToken = null;
+ List objectList = new ArrayList<>();
+ try {
+ while (isTruncated) {
+ if (continuationToken != null) {
+ listObjectsV2Request.setContinuationToken(continuationToken);
+ }
+
+ // 列举文件。
+ ListObjectsV2Result result = s3Client.listObjectsV2(listObjectsV2Request);
+
+ objectList.addAll(result.getObjectSummaries());
+
+ isTruncated = result.isTruncated();
+ continuationToken = result.getNextContinuationToken();
+ }
+ return objectList.stream().map(item -> {
+ StorageFileObject object = new StorageFileObject();
+ object.setPath(getRelativePath(item.getKey().substring(0, item.getKey().lastIndexOf("/"))));
+ object.setName(item.getKey().substring(item.getKey().lastIndexOf("/") + 1));
+ object.setSize(item.getSize());
+ object.setRawObject(item);
+ return object;
+ }).collect(Collectors.toList());
+ } catch (Exception e) {
+ throw new StorageException("列举文件失败:" + e.getMessage());
+ }
+ }
+
+ @Override
+ public boolean deleteDir(String... path) {
+ List objectList = listDir(buildPath(path));
+ AmazonS3 s3Client = getS3Client();
+ if (objectList.isEmpty()) {
+ return true;
+ }
+ int idx = 0;
+ int batchSize = 999;
+ while (objectList.size() > idx) {
+ if (objectList.size() - idx < batchSize) {
+ batchSize = objectList.size() - idx;
+ }
+ List subList = objectList.subList(idx, idx + batchSize);
+ idx += batchSize;
+ DeleteObjectsRequest request = new DeleteObjectsRequest(config.getBucketName())
+ .withKeys(subList.stream().map(StorageFileObject::getFullPath).toArray(String[]::new));
+ try {
+ s3Client.deleteObjects(request);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private AmazonS3Client getS3Client() {
+ BasicAWSCredentials basicAwsCred = new BasicAWSCredentials(config.getAccessKeyId(), config.getAccessKeySecret());
+ ClientConfiguration clientConfiguration = new ClientConfiguration();
+ clientConfiguration.setProtocol(Protocol.HTTPS);
+ AmazonS3Client s3 = new AmazonS3Client(basicAwsCred,clientConfiguration);
+ S3ClientOptions options = S3ClientOptions.builder().setPathStyleAccess(true).setPayloadSigningEnabled(true).disableChunkedEncoding().build();
+ s3.setS3ClientOptions(options);
+ s3.setEndpoint(config.getEndpoint());
+ return s3;
+ }
+
+ private String buildPath(String... paths) {
+ if (StringUtils.isNotBlank(config.getPrefix())) {
+ return StorageUtil.joinPath(config.getPrefix(), paths);
+ } else {
+ return StorageUtil.joinPath(paths);
+ }
+ }
+
+ private String getRelativePath(String path) {
+ return StorageUtil.getRelativePath(path, config.getPrefix());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ycwl/basic/storage/entity/AwsOssStorageConfig.java b/src/main/java/com/ycwl/basic/storage/entity/AwsOssStorageConfig.java
new file mode 100644
index 0000000..643871c
--- /dev/null
+++ b/src/main/java/com/ycwl/basic/storage/entity/AwsOssStorageConfig.java
@@ -0,0 +1,35 @@
+package com.ycwl.basic.storage.entity;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AwsOssStorageConfig extends StorageConfig {
+ private String endpoint;
+ private String accessKeyId;
+ private String accessKeySecret;
+ private String bucketName;
+ private String url;
+ private String region;
+ private String prefix;
+
+ @Override
+ public void checkEverythingOK() {
+ // TODO: 检查配置是否正确
+ }
+
+ public String getUrl() {
+ String url = this.url;
+ if (url == null) {
+ url = bucketName + "." + endpoint;
+ }
+ if (!url.startsWith("http")) {
+ url = "https://" + url;
+ }
+ if (url.endsWith("/")) {
+ url = url.substring(0, url.length() - 1);
+ }
+ return url;
+ }
+}
diff --git a/src/main/java/com/ycwl/basic/storage/enums/StorageType.java b/src/main/java/com/ycwl/basic/storage/enums/StorageType.java
index e1bfa8a..d88a780 100644
--- a/src/main/java/com/ycwl/basic/storage/enums/StorageType.java
+++ b/src/main/java/com/ycwl/basic/storage/enums/StorageType.java
@@ -4,6 +4,7 @@ import lombok.Getter;
public enum StorageType {
LOCAL("LOCAL"),
+ AWS_OSS("AWS_OSS"),
ALI_OSS("ALI_OSS");
@Getter
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 54be5a7..bd43f48 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -157,6 +157,16 @@ storage:
prefix: "user-video/"
url: "https://oss.zhentuai.com"
region: "cn-shanghai"
+ - name: "chaosheng"
+ type: "AWS_OSS"
+ config:
+ endpoint: "https://obs-cq.cucloud.cn"
+ accessKeyId: "5E628198FFEC47CEAFC211C341C60F767900"
+ accessKeySecret: "944346D1940E4AC6B5FCF981C7E589116498"
+ bucketName: "wsaiphoto"
+ prefix: "user-video/"
+ url: "https://wsaiphoto.obs-cq.cucloud.cn"
+ region: "obs-cq"
#阿里云人脸检测
aliFace:
accessKeyId: "LTAI5tMwrmxVcUEKoH5QzLHx"
diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml
index 3ac802b..5928a25 100644
--- a/src/main/resources/application-prod.yml
+++ b/src/main/resources/application-prod.yml
@@ -157,6 +157,16 @@ storage:
prefix: "user-video/"
url: "https://oss.zhentuai.com"
region: "cn-shanghai"
+ - name: "chaosheng"
+ type: "AWS_OSS"
+ config:
+ endpoint: "https://obs-cq.cucloud.cn"
+ accessKeyId: "5E628198FFEC47CEAFC211C341C60F767900"
+ accessKeySecret: "944346D1940E4AC6B5FCF981C7E589116498"
+ bucketName: "wsaiphoto"
+ prefix: "user-video/"
+ url: "https://wsaiphoto.obs-cq.cucloud.cn"
+ region: "obs-cq"
#阿里云人脸检测
aliFace: