修复配置文件中的拼写错误,更新MinIO和阿里云OSS相关异常类,添加生成预签名URL的方法

This commit is contained in:
Dftre 2025-02-23 14:52:41 +08:00
parent ff16be4064
commit ca36af0868
14 changed files with 402 additions and 106 deletions

View File

@ -42,9 +42,9 @@ minio:
url: http://localhost:9000
accessKey:
secretKey:
buketName: ruoyi
bucketName: ruoyi
# SLAVE:
# url: http://127.0.0.1:9000
# accessKey:
# secretKey:
# buketName: ry
# bucketName: ry

View File

@ -6,6 +6,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Objects;
@ -16,17 +17,22 @@ import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.FileEntity;
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileOperateUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import jakarta.servlet.http.HttpServletRequest;
/**
* 磁盘文件操作实现类
*/
@Component("file:strategy:disk")
public class DiskFileService implements FileService {
private static final long URL_EXPIRATION = 3600 * 1000; // URL有效期1小时
@Override
public String upload(String filePath, MultipartFile file) throws Exception {
return upload(RuoYiConfig.getProfile(), filePath, file);
@ -46,15 +52,24 @@ public class DiskFileService implements FileService {
@Override
public InputStream downLoad(String filePath) throws Exception {
// 本地资源路径
// 标准化路径
String normalizedPath = normalizeFilePath(filePath);
// 获取本地存储根路径
String localPath = RuoYiConfig.getProfile();
// 数据库资源地址
String downloadPath = localPath + StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);
// 下载名称
File file = new File(downloadPath);
// 拼接完整路径确保分隔符正确
String fullPath = localPath + File.separator + normalizedPath;
// 创建文件对象并检查
File file = new File(fullPath);
if (!file.exists()) {
throw new FileNotFoundException("未找到文件");
throw new FileNotFoundException("文件不存在: " + fullPath);
}
if (!file.isFile()) {
throw new FileNotFoundException("不是有效的文件: " + fullPath);
}
return new FileInputStream(file);
}
@ -88,5 +103,61 @@ public class DiskFileService implements FileService {
fileEntity.setFileInputSteam(fileInputStream);
fileEntity.setByteCount(file.length());
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();
}
}

View File

@ -2,6 +2,7 @@ package com.ruoyi.common.service.file;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import org.springframework.web.multipart.MultipartFile;
@ -90,4 +91,13 @@ public interface FileService {
* @throws Exception
*/
public FileEntity getFile(String filePath) throws Exception;
/**
* 生成预签名URL
*
* @param filePath 文件路径
* @return 预签名URL
* @throws Exception 如果生成过程中出现错误
*/
public URL generatePresignedUrl(String filePath) throws Exception;
}

View File

@ -6,7 +6,7 @@ public class MinioClientProperties {
private String url;
private String accessKey;
private String secretKey;
private String buketName;
private String bucketName;
private MinioClient client;
@ -34,14 +34,6 @@ public class MinioClientProperties {
this.secretKey = secretKey;
}
public String getBuketName() {
return buketName;
}
public void setBuketName(String buketName) {
this.buketName = buketName;
}
public MinioClient getClient() {
return client;
}
@ -50,13 +42,21 @@ public class MinioClientProperties {
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.accessKey = accessKey;
this.secretKey = secretKey;
this.buketName = buketName;
this.bucketName = bucketName;
}
public MinioClientProperties() {
}
public String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
}

View File

@ -15,6 +15,8 @@ import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
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.MinioClient;
@ -34,7 +36,17 @@ public class MinioConfig implements InitializingBean {
@Override
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) {
throw new RuntimeException("Primary client " + primary + " does not exist");
}
@ -71,12 +83,34 @@ public class MinioConfig implements InitializingBean {
.credentials(props.getAccessKey(), props.getSecretKey())
.build();
}
MinioBucket minioBucket = new MinioBucket(client, props.getBuketName());
MinioBucket minioBucket = new MinioBucket(client, props.getBucketName());
validateMinioBucket(minioBucket);
logger.info("数据桶:{} - 链接成功", name);
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() {
return maxSize;
}

View File

@ -37,5 +37,5 @@ public class MinioController {
@PathVariable("client") String client,
@RequestBody MultipartFile file) throws Exception {
return MinioUtil.uploadFile(client, file);
}
}
}

View File

@ -89,7 +89,7 @@ public class MinioBucket {
MinioFileVO minioFileVO = new MinioFileVO();
minioFileVO.setFileInputSteam(inputStream);
minioFileVO.setByteCount(Convert.toLong(inputStream.headers().get("Content-Length"),null));
minioFileVO.setByteCount(Convert.toLong(inputStream.headers().get("Content-Length"), null));
minioFileVO.setFilePath(filePath);
minioFileVO.setObject(inputStream.object());
minioFileVO.setRegion(inputStream.region());

View File

@ -1,7 +1,16 @@
package com.ruoyi.middleware.minio.exception;
public class MinioClientErrorException extends RuntimeException{
public MinioClientErrorException(String msg){
/**
* 当与MinIO客户端交互过程中发生错误时抛出此异常
*/
public class MinioClientErrorException extends RuntimeException {
public MinioClientErrorException(String msg) {
super(msg);
}
public MinioClientErrorException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,4 +1,15 @@
package com.ruoyi.middleware.minio.exception;
public class MinioClientNotFundException extends RuntimeException{
/**
* 当尝试获取MinIO客户端实例但未能找到相应的配置或客户端实例时抛出此异常
*/
public class MinioClientNotFundException extends RuntimeException {
public MinioClientNotFundException(String msg) {
super(msg);
}
public MinioClientNotFundException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,7 +1,11 @@
package com.ruoyi.middleware.minio.service;
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.boot.autoconfigure.condition.ConditionalOnProperty;
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.middleware.minio.config.MinioConfig;
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 io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.http.Method;
/**
* Minio文件操作实现类
*/
@Component("file:strategy:minio")
@ConditionalOnProperty(prefix = "minio", name = { "enable" }, havingValue = "true", matchIfMissing = false)
public class MinioFileService implements FileService {
private static final Logger logger = LoggerFactory.getLogger(MinioFileService.class);
@Autowired
private MinioConfig minioConfig;
@ -57,5 +68,35 @@ public class MinioFileService implements FileService {
@Override
public FileEntity getFile(String filePath) throws Exception {
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);
}
}
}

View File

@ -15,18 +15,146 @@ import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.ruoyi.alibaba.oss.domain.AliOssBucket;
import com.ruoyi.alibaba.oss.exception.AliOssClientErrorException;
import com.ruoyi.alibaba.oss.exception.AliOssClientNotFundException;
/**
* 配置类用于管理阿里云OSS客户端实例及其相关属性
*/
@Configuration("AliOssConfiguration")
@ConditionalOnProperty(prefix = "oss", name = "enable", havingValue = "true", matchIfMissing = false)
@ConfigurationProperties(prefix = "oss")
public class AliOssConfig implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(AliOssConfig.class);
public static int maxSize;
public static int maxSize; // 最大文件大小或其他配置项
private String prefix = "/oss"; // 根据需要调整前缀
private Map<String, AliOssProperties> client = new HashMap<>();
private String primary;
private Map<String, AliOssBucket> targetAliOssBucket = new HashMap<>();
private AliOssBucket masterBucket;
private Map<String, AliOssProperties> client = new HashMap<>(); // 存储所有OSS客户端配置信息
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() {
return maxSize;
@ -63,73 +191,4 @@ public class AliOssConfig implements InitializingBean {
public void setPrefix(String 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; // 或者抛出异常根据业务需求决定
}
}

View File

@ -14,9 +14,7 @@ public class AliOssProperties {
private OSSClient ossClient;
public AliOssProperties(){
}
public AliOssProperties(){ }
public AliOssProperties(String endpoint, String accessKeyId, String accessKeySecret, String bucketName) {
this.endpoint = endpoint;

View File

@ -1,4 +1,27 @@
package com.ruoyi.alibaba.oss.exception;
public class AliOssClientNotFundException extends RuntimeException{
/**
* 当尝试获取阿里云OSS客户端实例但未能找到相应的配置或客户端实例时抛出此异常
* 此异常表明系统中存在配置问题或者客户端初始化失败的问题
*/
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);
}
}

View File

@ -1,14 +1,22 @@
package com.ruoyi.alibaba.oss.service;
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
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.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.common.core.domain.entity.FileEntity;
import com.ruoyi.common.service.file.FileService;
@ -21,6 +29,9 @@ import com.ruoyi.common.utils.file.FileUtils;
@Component("file:strategy:oss")
@ConditionalOnProperty(prefix = "oss", name = { "enable" }, havingValue = "true", matchIfMissing = false)
public class AliOssFileService implements FileService {
private static final Logger logger = LoggerFactory.getLogger(AliOssConfig.class);
@Autowired
private AliOssConfig aliOssConfig;
@ -57,4 +68,33 @@ public class AliOssFileService implements FileService {
public FileEntity getFile(String filePath) throws Exception {
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客户端资源
}
}
}
}