修复配置文件中的拼写错误,更新MinIO和阿里云OSS相关异常类,添加生成预签名URL的方法
This commit is contained in:
parent
ff16be4064
commit
ca36af0868
@ -42,9 +42,9 @@ minio:
|
|||||||
url: http://localhost:9000
|
url: http://localhost:9000
|
||||||
accessKey:
|
accessKey:
|
||||||
secretKey:
|
secretKey:
|
||||||
buketName: ruoyi
|
bucketName: ruoyi
|
||||||
# SLAVE:
|
# SLAVE:
|
||||||
# url: http://127.0.0.1:9000
|
# url: http://127.0.0.1:9000
|
||||||
# accessKey:
|
# accessKey:
|
||||||
# secretKey:
|
# secretKey:
|
||||||
# buketName: ry
|
# bucketName: ry
|
||||||
|
@ -6,6 +6,7 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@ -16,17 +17,22 @@ import com.ruoyi.common.config.RuoYiConfig;
|
|||||||
import com.ruoyi.common.constant.Constants;
|
import com.ruoyi.common.constant.Constants;
|
||||||
import com.ruoyi.common.core.domain.entity.FileEntity;
|
import com.ruoyi.common.core.domain.entity.FileEntity;
|
||||||
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
|
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
|
||||||
|
import com.ruoyi.common.utils.ServletUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.common.utils.file.FileOperateUtils;
|
import com.ruoyi.common.utils.file.FileOperateUtils;
|
||||||
import com.ruoyi.common.utils.file.FileUtils;
|
import com.ruoyi.common.utils.file.FileUtils;
|
||||||
import com.ruoyi.common.utils.sign.Md5Utils;
|
import com.ruoyi.common.utils.sign.Md5Utils;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 磁盘文件操作实现类
|
* 磁盘文件操作实现类
|
||||||
*/
|
*/
|
||||||
@Component("file:strategy:disk")
|
@Component("file:strategy:disk")
|
||||||
public class DiskFileService implements FileService {
|
public class DiskFileService implements FileService {
|
||||||
|
|
||||||
|
private static final long URL_EXPIRATION = 3600 * 1000; // URL有效期1小时
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String upload(String filePath, MultipartFile file) throws Exception {
|
public String upload(String filePath, MultipartFile file) throws Exception {
|
||||||
return upload(RuoYiConfig.getProfile(), filePath, file);
|
return upload(RuoYiConfig.getProfile(), filePath, file);
|
||||||
@ -46,15 +52,24 @@ public class DiskFileService implements FileService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream downLoad(String filePath) throws Exception {
|
public InputStream downLoad(String filePath) throws Exception {
|
||||||
// 本地资源路径
|
// 标准化路径
|
||||||
|
String normalizedPath = normalizeFilePath(filePath);
|
||||||
|
|
||||||
|
// 获取本地存储根路径
|
||||||
String localPath = RuoYiConfig.getProfile();
|
String localPath = RuoYiConfig.getProfile();
|
||||||
// 数据库资源地址
|
|
||||||
String downloadPath = localPath + StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);
|
// 拼接完整路径,确保分隔符正确
|
||||||
// 下载名称
|
String fullPath = localPath + File.separator + normalizedPath;
|
||||||
File file = new File(downloadPath);
|
|
||||||
|
// 创建文件对象并检查
|
||||||
|
File file = new File(fullPath);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
throw new FileNotFoundException("未找到文件");
|
throw new FileNotFoundException("文件不存在: " + fullPath);
|
||||||
}
|
}
|
||||||
|
if (!file.isFile()) {
|
||||||
|
throw new FileNotFoundException("不是有效的文件: " + fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
return new FileInputStream(file);
|
return new FileInputStream(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,5 +103,61 @@ public class DiskFileService implements FileService {
|
|||||||
fileEntity.setFileInputSteam(fileInputStream);
|
fileEntity.setFileInputSteam(fileInputStream);
|
||||||
fileEntity.setByteCount(file.length());
|
fileEntity.setByteCount(file.length());
|
||||||
return fileEntity;
|
return fileEntity;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL generatePresignedUrl(String filePath) throws Exception {
|
||||||
|
try {
|
||||||
|
// 生成临时访问凭证
|
||||||
|
String normalizedPath = normalizeFilePath(filePath);
|
||||||
|
long expireTime = System.currentTimeMillis() + URL_EXPIRATION;
|
||||||
|
String toHex = Md5Utils.hash(normalizedPath + expireTime);
|
||||||
|
|
||||||
|
// 构建访问URL
|
||||||
|
String urlString = getUrl() +
|
||||||
|
"/common/download/resource?resource=" +
|
||||||
|
normalizedPath +
|
||||||
|
"&toHex=" + toHex +
|
||||||
|
"&expires=" + expireTime;
|
||||||
|
return new URL(urlString);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("生成访问URL失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准化文件路径
|
||||||
|
*/
|
||||||
|
private String normalizeFilePath(String filePath) {
|
||||||
|
if (StringUtils.isEmpty(filePath)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// 统一使用正斜杠并去除前缀
|
||||||
|
String normalizedPath = filePath.replace('\\', '/')
|
||||||
|
.replace("ruoyi/uploadPath/", "")
|
||||||
|
.replace("/profile/", "");
|
||||||
|
// 去除开头和结尾的斜杠
|
||||||
|
normalizedPath = StringUtils.strip(normalizedPath, "/");
|
||||||
|
// 处理文件存储重复的斜杠
|
||||||
|
normalizedPath = normalizedPath.replaceAll("/+", "/");
|
||||||
|
return normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取完整的请求路径,包括:域名,端口,上下文访问路径
|
||||||
|
*
|
||||||
|
* @return 服务地址
|
||||||
|
*/
|
||||||
|
public String getUrl()
|
||||||
|
{
|
||||||
|
HttpServletRequest request = ServletUtils.getRequest();
|
||||||
|
return getDomain(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDomain(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
StringBuffer url = request.getRequestURL();
|
||||||
|
String contextPath = request.getSession().getServletContext().getContextPath();
|
||||||
|
return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.ruoyi.common.service.file;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@ -90,4 +91,13 @@ public interface FileService {
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public FileEntity getFile(String filePath) throws Exception;
|
public FileEntity getFile(String filePath) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成预签名URL
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @return 预签名URL
|
||||||
|
* @throws Exception 如果生成过程中出现错误
|
||||||
|
*/
|
||||||
|
public URL generatePresignedUrl(String filePath) throws Exception;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ public class MinioClientProperties {
|
|||||||
private String url;
|
private String url;
|
||||||
private String accessKey;
|
private String accessKey;
|
||||||
private String secretKey;
|
private String secretKey;
|
||||||
private String buketName;
|
private String bucketName;
|
||||||
|
|
||||||
private MinioClient client;
|
private MinioClient client;
|
||||||
|
|
||||||
@ -34,14 +34,6 @@ public class MinioClientProperties {
|
|||||||
this.secretKey = secretKey;
|
this.secretKey = secretKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBuketName() {
|
|
||||||
return buketName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBuketName(String buketName) {
|
|
||||||
this.buketName = buketName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MinioClient getClient() {
|
public MinioClient getClient() {
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
@ -50,13 +42,21 @@ public class MinioClientProperties {
|
|||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MinioClientProperties(String url, String accessKey, String secretKey, String buketName) {
|
public MinioClientProperties(String url, String accessKey, String secretKey, String bucketName) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.accessKey = accessKey;
|
this.accessKey = accessKey;
|
||||||
this.secretKey = secretKey;
|
this.secretKey = secretKey;
|
||||||
this.buketName = buketName;
|
this.bucketName = bucketName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MinioClientProperties() {
|
public MinioClientProperties() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getBucketName() {
|
||||||
|
return bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBucketName(String bucketName) {
|
||||||
|
this.bucketName = bucketName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ import com.ruoyi.common.config.RuoYiConfig;
|
|||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.common.utils.file.FileUtils;
|
import com.ruoyi.common.utils.file.FileUtils;
|
||||||
import com.ruoyi.middleware.minio.domain.MinioBucket;
|
import com.ruoyi.middleware.minio.domain.MinioBucket;
|
||||||
|
import com.ruoyi.middleware.minio.exception.MinioClientErrorException;
|
||||||
|
import com.ruoyi.middleware.minio.exception.MinioClientNotFundException;
|
||||||
|
|
||||||
import io.minio.BucketExistsArgs;
|
import io.minio.BucketExistsArgs;
|
||||||
import io.minio.MinioClient;
|
import io.minio.MinioClient;
|
||||||
@ -34,7 +36,17 @@ public class MinioConfig implements InitializingBean {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
client.forEach((name, props) -> targetMinioBucket.put(name, createMinioClient(name, props)));
|
if (client == null || client.isEmpty()) {
|
||||||
|
throw new RuntimeException("Client properties cannot be null or empty");
|
||||||
|
}
|
||||||
|
client.forEach((name, props) -> {
|
||||||
|
try {
|
||||||
|
targetMinioBucket.put(name, createMinioClient(name, props));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to create MinIO client for {}: {}", name, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (targetMinioBucket.get(primary) == null) {
|
if (targetMinioBucket.get(primary) == null) {
|
||||||
throw new RuntimeException("Primary client " + primary + " does not exist");
|
throw new RuntimeException("Primary client " + primary + " does not exist");
|
||||||
}
|
}
|
||||||
@ -71,12 +83,34 @@ public class MinioConfig implements InitializingBean {
|
|||||||
.credentials(props.getAccessKey(), props.getSecretKey())
|
.credentials(props.getAccessKey(), props.getSecretKey())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
MinioBucket minioBucket = new MinioBucket(client, props.getBuketName());
|
MinioBucket minioBucket = new MinioBucket(client, props.getBucketName());
|
||||||
validateMinioBucket(minioBucket);
|
validateMinioBucket(minioBucket);
|
||||||
logger.info("数据桶:{} - 链接成功", name);
|
logger.info("数据桶:{} - 链接成功", name);
|
||||||
return minioBucket;
|
return minioBucket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主配置信息创建并返回MinIO客户端实例。
|
||||||
|
*
|
||||||
|
* @return MinioClient 实例
|
||||||
|
* @throws MinioClientNotFundException 如果找不到对应的配置时抛出
|
||||||
|
* @throws MinioClientErrorException 如果在创建过程中发生错误时抛出
|
||||||
|
*/
|
||||||
|
public MinioClient getPrimaryMinioClient() throws MinioClientNotFundException, MinioClientErrorException {
|
||||||
|
MinioClientProperties primaryClientProps = this.getClient().get(this.getPrimary());
|
||||||
|
if (primaryClientProps == null) {
|
||||||
|
throw new MinioClientNotFundException("未找到该Minio对象存储服务!");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return MinioClient.builder()
|
||||||
|
.endpoint(primaryClientProps.getUrl())
|
||||||
|
.credentials(primaryClientProps.getAccessKey(), primaryClientProps.getSecretKey())
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MinioClientErrorException("创建MinIO客户端失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int getMaxSize() {
|
public int getMaxSize() {
|
||||||
return maxSize;
|
return maxSize;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
package com.ruoyi.middleware.minio.exception;
|
package com.ruoyi.middleware.minio.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当与MinIO客户端交互过程中发生错误时抛出此异常。
|
||||||
|
*/
|
||||||
public class MinioClientErrorException extends RuntimeException {
|
public class MinioClientErrorException extends RuntimeException {
|
||||||
|
|
||||||
public MinioClientErrorException(String msg) {
|
public MinioClientErrorException(String msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MinioClientErrorException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,15 @@
|
|||||||
package com.ruoyi.middleware.minio.exception;
|
package com.ruoyi.middleware.minio.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当尝试获取MinIO客户端实例但未能找到相应的配置或客户端实例时抛出此异常。
|
||||||
|
*/
|
||||||
public class MinioClientNotFundException extends RuntimeException {
|
public class MinioClientNotFundException extends RuntimeException {
|
||||||
|
|
||||||
|
public MinioClientNotFundException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MinioClientNotFundException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package com.ruoyi.middleware.minio.service;
|
package com.ruoyi.middleware.minio.service;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -13,14 +17,21 @@ import com.ruoyi.common.utils.file.FileOperateUtils;
|
|||||||
import com.ruoyi.common.utils.file.FileUtils;
|
import com.ruoyi.common.utils.file.FileUtils;
|
||||||
import com.ruoyi.middleware.minio.config.MinioConfig;
|
import com.ruoyi.middleware.minio.config.MinioConfig;
|
||||||
import com.ruoyi.middleware.minio.domain.MinioFileVO;
|
import com.ruoyi.middleware.minio.domain.MinioFileVO;
|
||||||
|
import com.ruoyi.middleware.minio.exception.MinioClientErrorException;
|
||||||
|
import com.ruoyi.middleware.minio.exception.MinioClientNotFundException;
|
||||||
import com.ruoyi.middleware.minio.utils.MinioUtil;
|
import com.ruoyi.middleware.minio.utils.MinioUtil;
|
||||||
|
|
||||||
|
import io.minio.GetPresignedObjectUrlArgs;
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import io.minio.http.Method;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minio文件操作实现类
|
* Minio文件操作实现类
|
||||||
*/
|
*/
|
||||||
@Component("file:strategy:minio")
|
@Component("file:strategy:minio")
|
||||||
@ConditionalOnProperty(prefix = "minio", name = { "enable" }, havingValue = "true", matchIfMissing = false)
|
@ConditionalOnProperty(prefix = "minio", name = { "enable" }, havingValue = "true", matchIfMissing = false)
|
||||||
public class MinioFileService implements FileService {
|
public class MinioFileService implements FileService {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MinioFileService.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MinioConfig minioConfig;
|
private MinioConfig minioConfig;
|
||||||
@ -57,5 +68,35 @@ public class MinioFileService implements FileService {
|
|||||||
@Override
|
@Override
|
||||||
public FileEntity getFile(String filePath) throws Exception {
|
public FileEntity getFile(String filePath) throws Exception {
|
||||||
return MinioUtil.getFile(minioConfig.getPrimary(), filePath);
|
return MinioUtil.getFile(minioConfig.getPrimary(), filePath);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成预签名Minio URL.
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @return 预签名URL
|
||||||
|
* @throws MinioClientNotFundException 如果找不到对应的配置时抛出
|
||||||
|
* @throws MinioClientErrorException 如果在创建或获取预签名URL过程中发生错误时抛出
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL generatePresignedUrl(String filePath) throws MinioClientNotFundException, MinioClientErrorException {
|
||||||
|
MinioClient minioClient = null; // 创建并且实例化
|
||||||
|
try {
|
||||||
|
minioClient = minioConfig.getPrimaryMinioClient(); // 调用封装好的MinioConfig中的方法获取Minio客户端
|
||||||
|
String bucketName = minioConfig.getClient().get(minioConfig.getPrimary()).getBucketName();
|
||||||
|
GetPresignedObjectUrlArgs request = GetPresignedObjectUrlArgs.builder()
|
||||||
|
.method(Method.GET)
|
||||||
|
.bucket(bucketName)
|
||||||
|
.object(filePath)
|
||||||
|
.expiry(1, TimeUnit.HOURS) // 设置过期时间为1小时
|
||||||
|
.build();
|
||||||
|
// 生成预签名URL
|
||||||
|
String presignedUrl = minioClient.getPresignedObjectUrl(request);
|
||||||
|
URL url = new URL(presignedUrl); // 将字符串形式的预签名URL转换为URL对象并返回
|
||||||
|
return url;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("生成Minio预签名URL失败: {}", e.getMessage(), e); // 添加日志记录
|
||||||
|
throw new MinioClientErrorException("生成Minio预签名URL失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,18 +15,146 @@ import com.aliyun.oss.OSS;
|
|||||||
import com.aliyun.oss.OSSClientBuilder;
|
import com.aliyun.oss.OSSClientBuilder;
|
||||||
import com.aliyun.oss.OSSException;
|
import com.aliyun.oss.OSSException;
|
||||||
import com.ruoyi.alibaba.oss.domain.AliOssBucket;
|
import com.ruoyi.alibaba.oss.domain.AliOssBucket;
|
||||||
|
import com.ruoyi.alibaba.oss.exception.AliOssClientErrorException;
|
||||||
|
import com.ruoyi.alibaba.oss.exception.AliOssClientNotFundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置类用于管理阿里云OSS客户端实例及其相关属性。
|
||||||
|
*/
|
||||||
@Configuration("AliOssConfiguration")
|
@Configuration("AliOssConfiguration")
|
||||||
@ConditionalOnProperty(prefix = "oss", name = "enable", havingValue = "true", matchIfMissing = false)
|
@ConditionalOnProperty(prefix = "oss", name = "enable", havingValue = "true", matchIfMissing = false)
|
||||||
@ConfigurationProperties(prefix = "oss")
|
@ConfigurationProperties(prefix = "oss")
|
||||||
public class AliOssConfig implements InitializingBean {
|
public class AliOssConfig implements InitializingBean {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(AliOssConfig.class);
|
private static final Logger logger = LoggerFactory.getLogger(AliOssConfig.class);
|
||||||
public static int maxSize;
|
|
||||||
|
public static int maxSize; // 最大文件大小或其他配置项
|
||||||
|
|
||||||
private String prefix = "/oss"; // 根据需要调整前缀
|
private String prefix = "/oss"; // 根据需要调整前缀
|
||||||
private Map<String, AliOssProperties> client = new HashMap<>();
|
|
||||||
private String primary;
|
private Map<String, AliOssProperties> client = new HashMap<>(); // 存储所有OSS客户端配置信息
|
||||||
private Map<String, AliOssBucket> targetAliOssBucket = new HashMap<>();
|
|
||||||
private AliOssBucket masterBucket;
|
private String primary; // 主要使用的OSS客户端名称
|
||||||
|
|
||||||
|
private Map<String, AliOssBucket> targetAliOssBucket = new HashMap<>(); // 存储已创建的OSS客户端与桶名映射
|
||||||
|
|
||||||
|
private AliOssBucket masterBucket; // 主要使用的OSS客户端对应的桶对象
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化后检查客户端配置是否正确,并创建相应的OSS客户端实例。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
if (client == null || client.isEmpty()) {
|
||||||
|
throw new RuntimeException("Client properties cannot be null or empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
client.forEach((name, props) -> {
|
||||||
|
try {
|
||||||
|
AliOssBucket aliOssBucket = createOssClient(name, props);
|
||||||
|
targetAliOssBucket.put(name, aliOssBucket);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to create OSS client for {}: {}", name, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (targetAliOssBucket.get(primary) == null) {
|
||||||
|
throw new RuntimeException("Primary client " + primary + " does not exist");
|
||||||
|
}
|
||||||
|
masterBucket = targetAliOssBucket.get(primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个指定名称的OSS客户端实例。
|
||||||
|
*
|
||||||
|
* @param name 客户端名称
|
||||||
|
* @param props 客户端配置属性
|
||||||
|
* @return 包含OSS客户端和桶名的对象
|
||||||
|
*/
|
||||||
|
private AliOssBucket createOssClient(String name, AliOssProperties props) {
|
||||||
|
if (props == null || props.getEndpoint() == null || props.getAccessKeyId() == null ||
|
||||||
|
props.getAccessKeySecret() == null || props.getBucketName() == null) {
|
||||||
|
throw new IllegalArgumentException("AliOssProperties or its required fields cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
OSS client = new OSSClientBuilder().build(props.getEndpoint(), props.getAccessKeyId(),
|
||||||
|
props.getAccessKeySecret());
|
||||||
|
AliOssBucket ossBucket = new AliOssBucket(client, props.getBucketName());
|
||||||
|
validateOssBucket(ossBucket);
|
||||||
|
logger.info("数据桶:{} - 链接成功", name);
|
||||||
|
return ossBucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证给定的OSS桶是否存在。
|
||||||
|
*
|
||||||
|
* @param aliOssBucket 包含OSS客户端和桶名的对象
|
||||||
|
*/
|
||||||
|
private static void validateOssBucket(AliOssBucket aliOssBucket) {
|
||||||
|
OSS ossClient = aliOssBucket.getOssClient();
|
||||||
|
String bucketName = aliOssBucket.getBucketName();
|
||||||
|
try {
|
||||||
|
if (!ossClient.doesBucketExist(bucketName)) {
|
||||||
|
throw new RuntimeException("Bucket " + bucketName + " does not exist");
|
||||||
|
}
|
||||||
|
} catch (OSSException oe) {
|
||||||
|
logger.error("OSSException: " + oe.getMessage(), oe);
|
||||||
|
throw new RuntimeException("OSS error: " + oe.getMessage());
|
||||||
|
} catch (ClientException ce) {
|
||||||
|
logger.error("ClientException: " + ce.getMessage(), ce);
|
||||||
|
throw new RuntimeException("Client error: " + ce.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Exception: " + e.getMessage(), e);
|
||||||
|
throw new RuntimeException("Error validating OSS bucket: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取主桶的名称。
|
||||||
|
*
|
||||||
|
* @return 主桶的名称,如果未设置则返回null
|
||||||
|
*/
|
||||||
|
public String getMasterBucketName() {
|
||||||
|
if (masterBucket != null) {
|
||||||
|
return masterBucket.getBucketName();
|
||||||
|
}
|
||||||
|
return null; // 或者抛出异常
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据客户端名称获取对应的桶名。
|
||||||
|
*
|
||||||
|
* @param client 客户端名称
|
||||||
|
* @return 对应的桶名,如果未找到则返回null
|
||||||
|
*/
|
||||||
|
public String getBucketName(String client) {
|
||||||
|
if (client != null && targetAliOssBucket.containsKey(client)) {
|
||||||
|
return targetAliOssBucket.get(client).getBucketName();
|
||||||
|
}
|
||||||
|
return null; // 或者抛出异常
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主配置信息创建并返回OSS客户端实例。
|
||||||
|
*
|
||||||
|
* @return OSS客户端实例
|
||||||
|
* @throws AliOssClientNotFundException 如果找不到对应配置时抛出
|
||||||
|
* @throws AliOssClientErrorException 如果在创建过程中发生错误时抛出
|
||||||
|
*/
|
||||||
|
public OSS getPrimaryOssClient() throws AliOssClientNotFundException, AliOssClientErrorException {
|
||||||
|
AliOssProperties primaryClientProps = this.getClient().get(this.getPrimary());
|
||||||
|
if (primaryClientProps == null) {
|
||||||
|
throw new AliOssClientNotFundException("未找到该Oss对象存储服务!");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new OSSClientBuilder().build(
|
||||||
|
primaryClientProps.getEndpoint(),
|
||||||
|
primaryClientProps.getAccessKeyId(),
|
||||||
|
primaryClientProps.getAccessKeySecret());
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 在尝试构建OSS客户端时发生任何异常,抛出 AliOssClientErrorException
|
||||||
|
throw new AliOssClientErrorException("创建OSS客户端失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int getMaxSize() {
|
public int getMaxSize() {
|
||||||
return maxSize;
|
return maxSize;
|
||||||
@ -63,73 +191,4 @@ public class AliOssConfig implements InitializingBean {
|
|||||||
public void setPrefix(String prefix) {
|
public void setPrefix(String prefix) {
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
|
||||||
if (client == null || client.isEmpty()) {
|
|
||||||
throw new RuntimeException("Client properties cannot be null or empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
client.forEach((name, props) -> {
|
|
||||||
try {
|
|
||||||
AliOssBucket aliOssBucket = createOssClient(name, props);
|
|
||||||
targetAliOssBucket.put(name, aliOssBucket);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Failed to create OSS client for {}: {}", name, e.getMessage(), e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (targetAliOssBucket.get(primary) == null) {
|
|
||||||
throw new RuntimeException("Primary client " + primary + " does not exist");
|
|
||||||
}
|
|
||||||
masterBucket = targetAliOssBucket.get(primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AliOssBucket createOssClient(String name, AliOssProperties props) {
|
|
||||||
if (props == null || props.getEndpoint() == null || props.getAccessKeyId() == null ||
|
|
||||||
props.getAccessKeySecret() == null || props.getBucketName() == null) {
|
|
||||||
throw new IllegalArgumentException("AliOssProperties or its required fields cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
OSS client = new OSSClientBuilder().build(props.getEndpoint(), props.getAccessKeyId(), props.getAccessKeySecret());
|
|
||||||
AliOssBucket ossBucket = new AliOssBucket(client, props.getBucketName());
|
|
||||||
validateOssBucket(ossBucket);
|
|
||||||
logger.info("数据桶:{} - 链接成功", name);
|
|
||||||
return ossBucket;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void validateOssBucket(AliOssBucket aliOssBucket) {
|
|
||||||
OSS ossClient = aliOssBucket.getOssClient();
|
|
||||||
String bucketName = aliOssBucket.getBucketName();
|
|
||||||
try {
|
|
||||||
if (!ossClient.doesBucketExist(bucketName)) {
|
|
||||||
throw new RuntimeException("Bucket " + bucketName + " does not exist");
|
|
||||||
}
|
|
||||||
} catch (OSSException oe) {
|
|
||||||
logger.error("OSSException: " + oe.getMessage(), oe);
|
|
||||||
throw new RuntimeException("OSS error: " + oe.getMessage());
|
|
||||||
} catch (ClientException ce) {
|
|
||||||
logger.error("ClientException: " + ce.getMessage(), ce);
|
|
||||||
throw new RuntimeException("Client error: " + ce.getMessage());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Exception: " + e.getMessage(), e);
|
|
||||||
throw new RuntimeException("Error validating OSS bucket: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getMasterBucketName() {
|
|
||||||
if (masterBucket != null) {
|
|
||||||
return masterBucket.getBucketName();
|
|
||||||
}
|
|
||||||
return null; // 或者抛出异常,根据业务需求决定
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBucketName(String client) {
|
|
||||||
if (client != null && targetAliOssBucket.containsKey(client)) {
|
|
||||||
return targetAliOssBucket.get(client).getBucketName();
|
|
||||||
}
|
|
||||||
return null; // 或者抛出异常,根据业务需求决定
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ public class AliOssProperties {
|
|||||||
|
|
||||||
private OSSClient ossClient;
|
private OSSClient ossClient;
|
||||||
|
|
||||||
public AliOssProperties(){
|
public AliOssProperties(){ }
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public AliOssProperties(String endpoint, String accessKeyId, String accessKeySecret, String bucketName) {
|
public AliOssProperties(String endpoint, String accessKeyId, String accessKeySecret, String bucketName) {
|
||||||
this.endpoint = endpoint;
|
this.endpoint = endpoint;
|
||||||
|
@ -1,4 +1,27 @@
|
|||||||
package com.ruoyi.alibaba.oss.exception;
|
package com.ruoyi.alibaba.oss.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当尝试获取阿里云OSS客户端实例但未能找到相应的配置或客户端实例时抛出此异常。
|
||||||
|
* 此异常表明系统中存在配置问题或者客户端初始化失败的问题。
|
||||||
|
*/
|
||||||
public class AliOssClientNotFundException extends RuntimeException {
|
public class AliOssClientNotFundException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用指定的详细信息创建一个新的 {@code AliOssClientNotFundException} 实例。
|
||||||
|
*
|
||||||
|
* @param msg 描述异常原因的信息。
|
||||||
|
*/
|
||||||
|
public AliOssClientNotFundException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用指定的详细信息和导致此异常的原因创建一个新的 {@code AliOssClientNotFundException} 实例。
|
||||||
|
*
|
||||||
|
* @param message 描述异常原因的信息。
|
||||||
|
* @param cause 导致此异常的根本原因。
|
||||||
|
*/
|
||||||
|
public AliOssClientNotFundException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
package com.ruoyi.alibaba.oss.service;
|
package com.ruoyi.alibaba.oss.service;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import com.aliyun.oss.OSS;
|
||||||
|
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
|
||||||
import com.ruoyi.alibaba.oss.config.AliOssConfig;
|
import com.ruoyi.alibaba.oss.config.AliOssConfig;
|
||||||
import com.ruoyi.alibaba.oss.domain.AliOssFileVO;
|
import com.ruoyi.alibaba.oss.domain.AliOssFileVO;
|
||||||
|
import com.ruoyi.alibaba.oss.exception.AliOssClientErrorException;
|
||||||
|
import com.ruoyi.alibaba.oss.exception.AliOssClientNotFundException;
|
||||||
import com.ruoyi.alibaba.oss.utils.AliOssUtil;
|
import com.ruoyi.alibaba.oss.utils.AliOssUtil;
|
||||||
import com.ruoyi.common.core.domain.entity.FileEntity;
|
import com.ruoyi.common.core.domain.entity.FileEntity;
|
||||||
import com.ruoyi.common.service.file.FileService;
|
import com.ruoyi.common.service.file.FileService;
|
||||||
@ -21,6 +29,9 @@ import com.ruoyi.common.utils.file.FileUtils;
|
|||||||
@Component("file:strategy:oss")
|
@Component("file:strategy:oss")
|
||||||
@ConditionalOnProperty(prefix = "oss", name = { "enable" }, havingValue = "true", matchIfMissing = false)
|
@ConditionalOnProperty(prefix = "oss", name = { "enable" }, havingValue = "true", matchIfMissing = false)
|
||||||
public class AliOssFileService implements FileService {
|
public class AliOssFileService implements FileService {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AliOssConfig.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AliOssConfig aliOssConfig;
|
private AliOssConfig aliOssConfig;
|
||||||
|
|
||||||
@ -57,4 +68,33 @@ public class AliOssFileService implements FileService {
|
|||||||
public FileEntity getFile(String filePath) throws Exception {
|
public FileEntity getFile(String filePath) throws Exception {
|
||||||
return AliOssUtil.getFile(filePath);
|
return AliOssUtil.getFile(filePath);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成预签名Oss URL.
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @return 预签名URL
|
||||||
|
* @throws AliOssClientNotFundException 如果找不到对应的配置时抛出
|
||||||
|
* @throws AliOssClientErrorException 如果在创建或获取预签名URL过程中发生错误时抛出
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL generatePresignedUrl(String filePath) throws AliOssClientNotFundException, AliOssClientErrorException {
|
||||||
|
OSS ossClient = null; // 创建并且实例化
|
||||||
|
try {
|
||||||
|
ossClient = aliOssConfig.getPrimaryOssClient(); // 调用封装好的AliOssConfig中的方法获取OSS客户端
|
||||||
|
String bucketName = aliOssConfig.getClient().get(aliOssConfig.getPrimary()).getBucketName();
|
||||||
|
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, filePath);
|
||||||
|
Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000); // 设置过期时间为1小时
|
||||||
|
request.setExpiration(expiration);
|
||||||
|
// 生成预签名URL
|
||||||
|
return ossClient.generatePresignedUrl(request);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("生成Oss预签名URL失败: {}", e.getMessage(), e); // 添加日志记录
|
||||||
|
throw new AliOssClientErrorException("生成Oss预签名URL失败: " + e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
if (ossClient != null) {
|
||||||
|
ossClient.shutdown(); // 手动关闭OSS客户端资源
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user