支持s3 storage

This commit is contained in:
Jerry Yan 2025-02-24 18:31:37 +08:00
parent c321b41ad7
commit 9dc5708d04
7 changed files with 267 additions and 0 deletions

12
pom.xml
View File

@ -175,6 +175,18 @@
<version>3.17.4</version> <version>3.17.4</version>
</dependency> </dependency>
<!-- S3对象存储 -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
<version>1.11.24</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.24</version>
</dependency>
<!-- 阿里云人脸识别 --> <!-- 阿里云人脸识别 -->
<dependency> <dependency>
<groupId>com.aliyun</groupId> <groupId>com.aliyun</groupId>

View File

@ -1,6 +1,7 @@
package com.ycwl.basic.storage; package com.ycwl.basic.storage;
import com.ycwl.basic.storage.adapters.AliOssAdapter; 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.IStorageAdapter;
import com.ycwl.basic.storage.adapters.LocalStorageAdapter; import com.ycwl.basic.storage.adapters.LocalStorageAdapter;
import com.ycwl.basic.storage.entity.StorageConfig; import com.ycwl.basic.storage.entity.StorageConfig;
@ -23,6 +24,8 @@ public class StorageFactory {
switch (storageType) { switch (storageType) {
case LOCAL: case LOCAL:
return new LocalStorageAdapter(); return new LocalStorageAdapter();
case AWS_OSS:
return new AwsOssAdapter();
case ALI_OSS: case ALI_OSS:
return new AliOssAdapter(); return new AliOssAdapter();
default: default:

View File

@ -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<String, String> _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<StorageFileObject> 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<S3ObjectSummary> 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<StorageFileObject> 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<StorageFileObject> 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());
}
}

View File

@ -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;
}
}

View File

@ -4,6 +4,7 @@ import lombok.Getter;
public enum StorageType { public enum StorageType {
LOCAL("LOCAL"), LOCAL("LOCAL"),
AWS_OSS("AWS_OSS"),
ALI_OSS("ALI_OSS"); ALI_OSS("ALI_OSS");
@Getter @Getter

View File

@ -157,6 +157,16 @@ storage:
prefix: "user-video/" prefix: "user-video/"
url: "https://oss.zhentuai.com" url: "https://oss.zhentuai.com"
region: "cn-shanghai" 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: aliFace:
accessKeyId: "LTAI5tMwrmxVcUEKoH5QzLHx" accessKeyId: "LTAI5tMwrmxVcUEKoH5QzLHx"

View File

@ -157,6 +157,16 @@ storage:
prefix: "user-video/" prefix: "user-video/"
url: "https://oss.zhentuai.com" url: "https://oss.zhentuai.com"
region: "cn-shanghai" 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: aliFace: