You've already forked FrameTour-BE
更新后的storageAdapter
This commit is contained in:
@@ -33,25 +33,37 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
final public class AliOssAdapter extends AStorageAdapter {
|
||||
private AliOssStorageConfig config;
|
||||
private volatile AliOssStorageConfig config;
|
||||
private volatile OSS ossClient;
|
||||
private final Object clientLock = new Object();
|
||||
private final Object configLock = new Object();
|
||||
|
||||
@Override
|
||||
public String identity() {
|
||||
return config.identity();
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
return "ALI_OSS_UNCONFIGURED";
|
||||
}
|
||||
return currentConfig.identity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadConfig(Map<String, String> _config) {
|
||||
AliOssStorageConfig config = new AliOssStorageConfig();
|
||||
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;
|
||||
AliOssStorageConfig newConfig = new AliOssStorageConfig();
|
||||
newConfig.setAccessKeyId(_config.get("accessKeyId"));
|
||||
newConfig.setAccessKeySecret(_config.get("accessKeySecret"));
|
||||
newConfig.setBucketName(_config.get("bucketName"));
|
||||
newConfig.setEndpoint(_config.get("endpoint"));
|
||||
newConfig.setRegion(_config.get("region"));
|
||||
newConfig.setUrl(_config.get("url"));
|
||||
newConfig.setPrefix(_config.get("prefix"));
|
||||
newConfig.checkEverythingOK();
|
||||
|
||||
synchronized (configLock) {
|
||||
this.config = newConfig;
|
||||
// 配置更新后,需要重置客户端连接
|
||||
resetClient();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,11 +71,15 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
if (config == null) {
|
||||
throw new StorageConfigException("配置为空");
|
||||
}
|
||||
if (config instanceof AliOssStorageConfig) {
|
||||
this.config = (AliOssStorageConfig) config;
|
||||
} else {
|
||||
if (!(config instanceof AliOssStorageConfig)) {
|
||||
throw new StorageConfigException("配置类型错误,传入的类为:" + config.getClass().getName());
|
||||
}
|
||||
|
||||
synchronized (configLock) {
|
||||
this.config = (AliOssStorageConfig) config;
|
||||
// 配置更新后,需要重置客户端连接
|
||||
resetClient();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,12 +91,25 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
try (OSSWrapper wrapper = getOssClient()) {
|
||||
OSS ossClient = wrapper.getOSSClient();
|
||||
ObjectMetadata metadata = new ObjectMetadata();
|
||||
metadata.setContentLength(inputStream.available());
|
||||
|
||||
// 读取完整内容并计算实际长度
|
||||
byte[] bytes = inputStream.readAllBytes();
|
||||
metadata.setContentLength(bytes.length);
|
||||
|
||||
if (StringUtils.isNotBlank(contentType)) {
|
||||
metadata.setContentType(contentType);
|
||||
}
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), fullPath, inputStream);
|
||||
ossClient.putObject(putObjectRequest);
|
||||
|
||||
// 使用字节数组创建新的 InputStream
|
||||
try (InputStream byteInputStream = new java.io.ByteArrayInputStream(bytes)) {
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
throw new StorageConfigException("存储适配器未配置");
|
||||
}
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(currentConfig.getBucketName(), fullPath, byteInputStream, metadata);
|
||||
ossClient.putObject(putObjectRequest);
|
||||
}
|
||||
|
||||
return getUrl(path);
|
||||
} catch (Exception e) {
|
||||
throw new UploadFileFailedException("上传文件失败:" + e.getMessage());
|
||||
@@ -92,7 +121,11 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
public boolean deleteFile(String... path) {
|
||||
try (OSSWrapper wrapper = getOssClient()) {
|
||||
OSS ossClient = wrapper.getOSSClient();
|
||||
ossClient.deleteObject(config.getBucketName(), buildPath(path));
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
return false;
|
||||
}
|
||||
ossClient.deleteObject(currentConfig.getBucketName(), buildPath(path));
|
||||
return true;
|
||||
} catch (ClientException e) {
|
||||
return false;
|
||||
@@ -101,14 +134,22 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
|
||||
@Override
|
||||
public String getUrl(String... path) {
|
||||
return config.getUrl() + "/" + buildPath(path);
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
throw new StorageConfigException("存储适配器未配置");
|
||||
}
|
||||
return currentConfig.getUrl() + "/" + buildPath(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlForDownload(Date expireDate, String... path) {
|
||||
try (OSSWrapper wrapper = getOssClient()) {
|
||||
OSS ossClient = wrapper.getOSSClient();
|
||||
URL url = ossClient.generatePresignedUrl(config.getBucketName(), buildPath(path), expireDate, HttpMethod.GET);
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
throw new StorageConfigException("存储适配器未配置");
|
||||
}
|
||||
URL url = ossClient.generatePresignedUrl(currentConfig.getBucketName(), buildPath(path), expireDate, HttpMethod.GET);
|
||||
return url.toString();
|
||||
}
|
||||
}
|
||||
@@ -117,7 +158,11 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
public String getUrlForUpload(Date expireDate, String contentType, String... path) {
|
||||
try (OSSWrapper wrapper = getOssClient()) {
|
||||
OSS ossClient = wrapper.getOSSClient();
|
||||
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(config.getBucketName(), buildPath(path), HttpMethod.PUT);
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
throw new StorageConfigException("存储适配器未配置");
|
||||
}
|
||||
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(currentConfig.getBucketName(), buildPath(path), HttpMethod.PUT);
|
||||
if (StringUtils.isNotBlank(contentType)) {
|
||||
request.setContentType(contentType);
|
||||
}
|
||||
@@ -129,7 +174,11 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
|
||||
@Override
|
||||
public List<StorageFileObject> listDir(String... path) {
|
||||
ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request(config.getBucketName());
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
throw new StorageConfigException("存储适配器未配置");
|
||||
}
|
||||
ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request(currentConfig.getBucketName());
|
||||
listObjectsV2Request.setPrefix(buildPath(path) + "/");
|
||||
listObjectsV2Request.setMaxKeys(1000);
|
||||
boolean isTruncated = true;
|
||||
@@ -180,7 +229,11 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
}
|
||||
List<StorageFileObject> subList = objectList.subList(idx, idx + batchSize);
|
||||
idx += batchSize;
|
||||
DeleteObjectsRequest request = new DeleteObjectsRequest(config.getBucketName());
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
return false;
|
||||
}
|
||||
DeleteObjectsRequest request = new DeleteObjectsRequest(currentConfig.getBucketName());
|
||||
request.setKeys(subList.stream().map(StorageFileObject::getFullPath).collect(Collectors.toList()));
|
||||
try {
|
||||
ossClient.deleteObjects(request);
|
||||
@@ -208,7 +261,11 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
public boolean setAcl(StorageAcl acl, String... path) {
|
||||
try (OSSWrapper wrapper = getOssClient()) {
|
||||
OSS ossClient = wrapper.getOSSClient();
|
||||
ossClient.setObjectAcl(config.getBucketName(), buildPath(path), convertAcl(acl));
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
return false;
|
||||
}
|
||||
ossClient.setObjectAcl(currentConfig.getBucketName(), buildPath(path), convertAcl(acl));
|
||||
return true;
|
||||
} catch (OSSException e) {
|
||||
return false;
|
||||
@@ -219,32 +276,85 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
public boolean isExists(String ...path) {
|
||||
try (OSSWrapper wrapper = getOssClient()) {
|
||||
OSS ossClient = wrapper.getOSSClient();
|
||||
return ossClient.doesObjectExist(config.getBucketName(), buildPath(path));
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
return false;
|
||||
}
|
||||
return ossClient.doesObjectExist(currentConfig.getBucketName(), buildPath(path));
|
||||
}
|
||||
}
|
||||
|
||||
private OSSWrapper getOssClient() {
|
||||
OSS ossClient = new OSSClientBuilder().build(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
|
||||
return new OSSWrapper(ossClient);
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
throw new StorageConfigException("存储适配器未配置");
|
||||
}
|
||||
|
||||
if (ossClient == null) {
|
||||
synchronized (clientLock) {
|
||||
if (ossClient == null) {
|
||||
// 在同步块内再次检查配置,确保配置一致性
|
||||
currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
throw new StorageConfigException("存储适配器未配置");
|
||||
}
|
||||
ossClient = new OSSClientBuilder().build(
|
||||
currentConfig.getEndpoint(),
|
||||
currentConfig.getAccessKeyId(),
|
||||
currentConfig.getAccessKeySecret()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new OSSWrapper(ossClient, false); // false 表示不自动关闭共享客户端
|
||||
}
|
||||
|
||||
// 重置客户端连接(配置更新时调用)
|
||||
private void resetClient() {
|
||||
synchronized (clientLock) {
|
||||
if (ossClient != null) {
|
||||
ossClient.shutdown();
|
||||
ossClient = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 手动关闭客户端的方法,用于适配器销毁时
|
||||
public void shutdown() {
|
||||
synchronized (clientLock) {
|
||||
if (ossClient != null) {
|
||||
ossClient.shutdown();
|
||||
ossClient = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildPath(String ...paths) {
|
||||
if (StringUtils.isNotBlank(config.getPrefix())) {
|
||||
return StorageUtil.joinPath(config.getPrefix(), paths);
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig != null && StringUtils.isNotBlank(currentConfig.getPrefix())) {
|
||||
return StorageUtil.joinPath(currentConfig.getPrefix(), paths);
|
||||
} else {
|
||||
return StorageUtil.joinPath(paths);
|
||||
}
|
||||
}
|
||||
|
||||
private String getRelativePath(String path) {
|
||||
return StorageUtil.getRelativePath(path, config.getPrefix());
|
||||
AliOssStorageConfig currentConfig = this.config;
|
||||
String prefix = currentConfig != null ? currentConfig.getPrefix() : null;
|
||||
return StorageUtil.getRelativePath(path, prefix);
|
||||
}
|
||||
|
||||
public static class OSSWrapper implements AutoCloseable {
|
||||
private final OSS ossClient;
|
||||
private final boolean autoClose;
|
||||
|
||||
public OSSWrapper(OSS ossClient) {
|
||||
this(ossClient, true);
|
||||
}
|
||||
|
||||
public OSSWrapper(OSS ossClient, boolean autoClose) {
|
||||
this.ossClient = ossClient;
|
||||
this.autoClose = autoClose;
|
||||
}
|
||||
|
||||
// 提供对原始对象的方法访问
|
||||
@@ -254,8 +364,10 @@ final public class AliOssAdapter extends AStorageAdapter {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// 在此处实现资源关闭逻辑(如调用 OSS 的关闭方法)
|
||||
ossClient.shutdown(); // 假设 OSS 提供 shutdown 方法关闭资源
|
||||
// 只有非共享客户端才自动关闭
|
||||
if (autoClose && ossClient != null) {
|
||||
ossClient.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -26,25 +26,37 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AwsOssAdapter extends AStorageAdapter {
|
||||
private AwsOssStorageConfig config;
|
||||
private volatile AwsOssStorageConfig config;
|
||||
private volatile AmazonS3Client s3Client;
|
||||
private final Object clientLock = new Object();
|
||||
private final Object configLock = new Object();
|
||||
|
||||
@Override
|
||||
public String identity() {
|
||||
return config.identity();
|
||||
AwsOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
return "AWS_OSS_UNCONFIGURED";
|
||||
}
|
||||
return currentConfig.identity();
|
||||
}
|
||||
|
||||
@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;
|
||||
AwsOssStorageConfig newConfig = new AwsOssStorageConfig();
|
||||
newConfig.setAccessKeyId(_config.get("accessKeyId"));
|
||||
newConfig.setAccessKeySecret(_config.get("accessKeySecret"));
|
||||
newConfig.setBucketName(_config.get("bucketName"));
|
||||
newConfig.setEndpoint(_config.get("endpoint"));
|
||||
newConfig.setRegion(_config.get("region"));
|
||||
newConfig.setUrl(_config.get("url"));
|
||||
newConfig.setPrefix(_config.get("prefix"));
|
||||
newConfig.checkEverythingOK();
|
||||
|
||||
synchronized (configLock) {
|
||||
this.config = newConfig;
|
||||
// 配置更新后,需要重置客户端连接
|
||||
resetClient();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,11 +64,15 @@ public class AwsOssAdapter extends AStorageAdapter {
|
||||
if (config == null) {
|
||||
throw new StorageConfigException("配置为空");
|
||||
}
|
||||
if (config instanceof AwsOssStorageConfig) {
|
||||
this.config = (AwsOssStorageConfig) config;
|
||||
} else {
|
||||
if (!(config instanceof AwsOssStorageConfig)) {
|
||||
throw new StorageConfigException("配置类型错误,传入的类为:" + config.getClass().getName());
|
||||
}
|
||||
|
||||
synchronized (configLock) {
|
||||
this.config = (AwsOssStorageConfig) config;
|
||||
// 配置更新后,需要重置客户端连接
|
||||
resetClient();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,13 +84,22 @@ public class AwsOssAdapter extends AStorageAdapter {
|
||||
try (S3Wrapper wrapper = getS3Client()) {
|
||||
AmazonS3Client s3Client = wrapper.s3Client();
|
||||
ObjectMetadata metadata = new ObjectMetadata();
|
||||
metadata.setContentLength(inputStream.available());
|
||||
|
||||
// 读取完整内容并计算实际长度
|
||||
byte[] bytes = inputStream.readAllBytes();
|
||||
metadata.setContentLength(bytes.length);
|
||||
|
||||
if (StringUtils.isNotBlank(contentType)) {
|
||||
metadata.setContentType(contentType);
|
||||
}
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), fullPath, inputStream, metadata);
|
||||
putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead); // 设置访问权限,让所有用户都允许访问
|
||||
s3Client.putObject(putObjectRequest);
|
||||
|
||||
// 使用字节数组创建新的 InputStream
|
||||
try (InputStream byteInputStream = new java.io.ByteArrayInputStream(bytes)) {
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), fullPath, byteInputStream, metadata);
|
||||
putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead); // 设置访问权限,让所有用户都允许访问
|
||||
s3Client.putObject(putObjectRequest);
|
||||
}
|
||||
|
||||
return getUrl(path);
|
||||
} catch (Exception e) {
|
||||
throw new UploadFileFailedException("上传文件失败:" + e.getMessage());
|
||||
@@ -150,6 +175,7 @@ public class AwsOssAdapter extends AStorageAdapter {
|
||||
object.setPath(getRelativePath(item.getKey().substring(0, item.getKey().lastIndexOf("/"))));
|
||||
object.setName(item.getKey().substring(item.getKey().lastIndexOf("/") + 1));
|
||||
object.setSize(item.getSize());
|
||||
object.setModifyTime(item.getLastModified());
|
||||
object.setRawObject(item);
|
||||
return object;
|
||||
}).collect(Collectors.toList());
|
||||
@@ -220,14 +246,57 @@ public class AwsOssAdapter extends AStorageAdapter {
|
||||
}
|
||||
|
||||
private S3Wrapper 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 new S3Wrapper(s3);
|
||||
AwsOssStorageConfig currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
throw new StorageConfigException("存储适配器未配置");
|
||||
}
|
||||
|
||||
if (s3Client == null) {
|
||||
synchronized (clientLock) {
|
||||
if (s3Client == null) {
|
||||
// 在同步块内再次检查配置,确保配置一致性
|
||||
currentConfig = this.config;
|
||||
if (currentConfig == null) {
|
||||
throw new StorageConfigException("存储适配器未配置");
|
||||
}
|
||||
BasicAWSCredentials basicAwsCred = new BasicAWSCredentials(
|
||||
currentConfig.getAccessKeyId(),
|
||||
currentConfig.getAccessKeySecret()
|
||||
);
|
||||
ClientConfiguration clientConfiguration = new ClientConfiguration();
|
||||
clientConfiguration.setProtocol(Protocol.HTTPS);
|
||||
s3Client = new AmazonS3Client(basicAwsCred, clientConfiguration);
|
||||
S3ClientOptions options = S3ClientOptions.builder()
|
||||
.setPathStyleAccess(true)
|
||||
.setPayloadSigningEnabled(true)
|
||||
.disableChunkedEncoding()
|
||||
.build();
|
||||
s3Client.setS3ClientOptions(options);
|
||||
s3Client.setEndpoint(currentConfig.getEndpoint());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new S3Wrapper(s3Client, false); // false 表示不自动关闭共享客户端
|
||||
}
|
||||
|
||||
// 重置客户端连接(配置更新时调用)
|
||||
private void resetClient() {
|
||||
synchronized (clientLock) {
|
||||
if (s3Client != null) {
|
||||
s3Client.shutdown();
|
||||
s3Client = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 手动关闭客户端的方法,用于适配器销毁时
|
||||
public void shutdown() {
|
||||
synchronized (clientLock) {
|
||||
if (s3Client != null) {
|
||||
s3Client.shutdown();
|
||||
s3Client = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildPath(String... paths) {
|
||||
@@ -242,10 +311,18 @@ public class AwsOssAdapter extends AStorageAdapter {
|
||||
return StorageUtil.getRelativePath(path, config.getPrefix());
|
||||
}
|
||||
|
||||
public record S3Wrapper(AmazonS3Client s3Client) implements AutoCloseable {
|
||||
public record S3Wrapper(AmazonS3Client s3Client, boolean autoClose) implements AutoCloseable {
|
||||
|
||||
public S3Wrapper(AmazonS3Client s3Client) {
|
||||
this(s3Client, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
s3Client.shutdown();
|
||||
// 只有非共享客户端才自动关闭
|
||||
if (autoClose && s3Client != null) {
|
||||
s3Client.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,71 +3,207 @@ package com.ycwl.basic.storage.adapters;
|
||||
import com.ycwl.basic.storage.entity.StorageConfig;
|
||||
import com.ycwl.basic.storage.entity.StorageFileObject;
|
||||
import com.ycwl.basic.storage.enums.StorageAcl;
|
||||
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.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.nio.file.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class LocalStorageAdapter extends AStorageAdapter {
|
||||
private volatile String basePath;
|
||||
private volatile String baseUrl;
|
||||
private volatile String prefix = "";
|
||||
private final Object configLock = new Object();
|
||||
|
||||
public class LocalStorageAdapter extends AStorageAdapter{
|
||||
@Override
|
||||
public String identity() {
|
||||
return "";
|
||||
return "LOCAL_STORAGE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadConfig(Map<String, String> config) {
|
||||
|
||||
String newBasePath = config.get("basePath");
|
||||
String newBaseUrl = config.get("baseUrl");
|
||||
String newPrefix = StringUtils.isNotBlank(config.get("prefix")) ? config.get("prefix") : "";
|
||||
|
||||
if (StringUtils.isBlank(newBasePath)) {
|
||||
throw new StorageConfigException("本地存储配置错误:basePath 不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(newBaseUrl)) {
|
||||
throw new StorageConfigException("本地存储配置错误:baseUrl 不能为空");
|
||||
}
|
||||
|
||||
// 确保基础目录存在
|
||||
try {
|
||||
Files.createDirectories(Paths.get(newBasePath));
|
||||
} catch (IOException e) {
|
||||
throw new StorageConfigException("创建本地存储目录失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
synchronized (configLock) {
|
||||
this.basePath = newBasePath;
|
||||
this.baseUrl = newBaseUrl;
|
||||
this.prefix = newPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(StorageConfig config) {
|
||||
|
||||
throw new StorageConfigException("LocalStorageAdapter 不支持 StorageConfig 类型配置");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadFile(String contentType, InputStream inputStream, String... path) {
|
||||
return "";
|
||||
if (inputStream == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String fullPath = buildLocalPath(path);
|
||||
try {
|
||||
// 确保父目录存在
|
||||
Path filePath = Paths.get(fullPath);
|
||||
Files.createDirectories(filePath.getParent());
|
||||
|
||||
// 复制文件内容到本地
|
||||
Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
return getUrl(path);
|
||||
} catch (IOException e) {
|
||||
throw new UploadFileFailedException("本地文件保存失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteFile(String... path) {
|
||||
return false;
|
||||
try {
|
||||
String fullPath = buildLocalPath(path);
|
||||
return Files.deleteIfExists(Paths.get(fullPath));
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl(String... path) {
|
||||
return "";
|
||||
String relativePath = buildRelativePath(path);
|
||||
return baseUrl + "/" + relativePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlForDownload(Date expireDate, String... path) {
|
||||
return "";
|
||||
// 本地存储不支持带过期时间的URL,直接返回普通URL
|
||||
return getUrl(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlForUpload(Date expireDate, String contentType, String... path) {
|
||||
return "";
|
||||
// 本地存储不支持预签名上传URL
|
||||
return getUrl(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StorageFileObject> listDir(String... path) {
|
||||
return Collections.emptyList();
|
||||
String fullPath = buildLocalPath(path);
|
||||
Path dirPath = Paths.get(fullPath);
|
||||
|
||||
if (!Files.exists(dirPath) || !Files.isDirectory(dirPath)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<StorageFileObject> result = new ArrayList<>();
|
||||
try (Stream<Path> paths = Files.list(dirPath)) {
|
||||
paths.forEach(filePath -> {
|
||||
try {
|
||||
StorageFileObject obj = new StorageFileObject();
|
||||
obj.setName(filePath.getFileName().toString());
|
||||
obj.setPath(getRelativePath(filePath.getParent().toString()));
|
||||
obj.setSize(Files.size(filePath));
|
||||
obj.setModifyTime(new Date(Files.getLastModifiedTime(filePath).toMillis()));
|
||||
obj.setRawObject(filePath.toFile());
|
||||
result.add(obj);
|
||||
} catch (IOException e) {
|
||||
// 忽略无法读取的文件
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new StorageException("列举本地文件失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteDir(String... path) {
|
||||
return false;
|
||||
String fullPath = buildLocalPath(path);
|
||||
Path dirPath = Paths.get(fullPath);
|
||||
|
||||
if (!Files.exists(dirPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// 递归删除目录及其内容
|
||||
try (Stream<Path> paths = Files.walk(dirPath)) {
|
||||
paths.sorted((a, b) -> b.compareTo(a)) // 从深到浅排序,确保先删除文件再删除目录
|
||||
.forEach(filePath -> {
|
||||
try {
|
||||
Files.deleteIfExists(filePath);
|
||||
} catch (IOException e) {
|
||||
// 忽略删除失败的文件
|
||||
}
|
||||
});
|
||||
}
|
||||
return !Files.exists(dirPath);
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setAcl(StorageAcl acl, String... path) {
|
||||
return false;
|
||||
// 本地存储不支持ACL设置
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExists(String... path) {
|
||||
return false;
|
||||
String fullPath = buildLocalPath(path);
|
||||
return Files.exists(Paths.get(fullPath));
|
||||
}
|
||||
|
||||
private String buildLocalPath(String... paths) {
|
||||
String relativePath = buildRelativePath(paths);
|
||||
return Paths.get(basePath, relativePath).toString();
|
||||
}
|
||||
|
||||
private String buildRelativePath(String... paths) {
|
||||
if (StringUtils.isNotBlank(prefix)) {
|
||||
return StorageUtil.joinPath(prefix, paths);
|
||||
} else {
|
||||
return StorageUtil.joinPath(paths);
|
||||
}
|
||||
}
|
||||
|
||||
private String getRelativePath(String fullPath) {
|
||||
String basePathNormalized = Paths.get(basePath).toString();
|
||||
if (fullPath.startsWith(basePathNormalized)) {
|
||||
String relative = fullPath.substring(basePathNormalized.length());
|
||||
if (relative.startsWith(File.separator)) {
|
||||
relative = relative.substring(1);
|
||||
}
|
||||
return StorageUtil.getRelativePath(relative, prefix);
|
||||
}
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user