Commit 97a981c2949027127934082bfb497e889c66438f

Authored by YunaiV
1 parent 4c5134ae

feat:【infra 基础设施】支持私有桶

yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java
@@ -47,8 +47,15 @@ public class HttpUtils { @@ -47,8 +47,15 @@ public class HttpUtils {
47 return builder.build(); 47 return builder.build();
48 } 48 }
49 49
50 - private String append(String base, Map<String, ?> query, boolean fragment) {  
51 - return append(base, query, null, fragment); 50 + public static String removeUrlQuery(String url) {
  51 + if (!StrUtil.contains(url, '?')) {
  52 + return url;
  53 + }
  54 + UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
  55 + // 移除 query、fragment
  56 + builder.setQuery(null);
  57 + builder.setFragment(null);
  58 + return builder.build();
52 } 59 }
53 60
54 /** 61 /**
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApi.java
@@ -42,4 +42,14 @@ public interface FileApi { @@ -42,4 +42,14 @@ public interface FileApi {
42 String createFile(@NotEmpty(message = "文件内容不能为空") byte[] content, 42 String createFile(@NotEmpty(message = "文件内容不能为空") byte[] content,
43 String name, String directory, String type); 43 String name, String directory, String type);
44 44
  45 + /**
  46 + * 生成文件预签名地址,用于读取
  47 + *
  48 + * @param url 完整的文件访问地址
  49 + * @param expirationSeconds 访问有效期,单位秒
  50 + * @return 文件预签名地址
  51 + */
  52 + String presignGetUrl(@NotEmpty(message = "URL 不能为空") String url,
  53 + Integer expirationSeconds);
  54 +
45 } 55 }
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApiImpl.java
@@ -22,4 +22,9 @@ public class FileApiImpl implements FileApi { @@ -22,4 +22,9 @@ public class FileApiImpl implements FileApi {
22 return fileService.createFile(content, name, directory, type); 22 return fileService.createFile(content, name, directory, type);
23 } 23 }
24 24
  25 + @Override
  26 + public String presignGetUrl(String url, Integer expirationSeconds) {
  27 + return fileService.presignGetUrl(url, expirationSeconds);
  28 + }
  29 +
25 } 30 }
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
@@ -51,7 +51,7 @@ public class FileController { @@ -51,7 +51,7 @@ public class FileController {
51 } 51 }
52 52
53 @GetMapping("/presigned-url") 53 @GetMapping("/presigned-url")
54 - @Operation(summary = "获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器") 54 + @Operation(summary = "获取文件预签名地址(上传)", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器")
55 @Parameters({ 55 @Parameters({
56 @Parameter(name = "name", description = "文件名称", required = true), 56 @Parameter(name = "name", description = "文件名称", required = true),
57 @Parameter(name = "directory", description = "文件目录") 57 @Parameter(name = "directory", description = "文件目录")
@@ -59,7 +59,7 @@ public class FileController { @@ -59,7 +59,7 @@ public class FileController {
59 public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl( 59 public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(
60 @RequestParam("name") String name, 60 @RequestParam("name") String name,
61 @RequestParam(value = "directory", required = false) String directory) { 61 @RequestParam(value = "directory", required = false) String directory) {
62 - return success(fileService.getFilePresignedUrl(name, directory)); 62 + return success(fileService.presignPutUrl(name, directory));
63 } 63 }
64 64
65 @PostMapping("/create") 65 @PostMapping("/create")
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/AppFileController.java
@@ -41,7 +41,7 @@ public class AppFileController { @@ -41,7 +41,7 @@ public class AppFileController {
41 } 41 }
42 42
43 @GetMapping("/presigned-url") 43 @GetMapping("/presigned-url")
44 - @Operation(summary = "获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器") 44 + @Operation(summary = "获取文件预签名地址(上传)", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器")
45 @Parameters({ 45 @Parameters({
46 @Parameter(name = "name", description = "文件名称", required = true), 46 @Parameter(name = "name", description = "文件名称", required = true),
47 @Parameter(name = "directory", description = "文件目录") 47 @Parameter(name = "directory", description = "文件目录")
@@ -49,7 +49,7 @@ public class AppFileController { @@ -49,7 +49,7 @@ public class AppFileController {
49 public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl( 49 public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(
50 @RequestParam("name") String name, 50 @RequestParam("name") String name,
51 @RequestParam(value = "directory", required = false) String directory) { 51 @RequestParam(value = "directory", required = false) String directory) {
52 - return success(fileService.getFilePresignedUrl(name, directory)); 52 + return success(fileService.presignPutUrl(name, directory));
53 } 53 }
54 54
55 @PostMapping("/create") 55 @PostMapping("/create")
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java
1 package cn.iocoder.yudao.module.infra.framework.file.core.client; 1 package cn.iocoder.yudao.module.infra.framework.file.core.client;
2 2
3 -import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;  
4 -  
5 /** 3 /**
6 * 文件客户端 4 * 文件客户端
7 * 5 *
@@ -42,13 +40,26 @@ public interface FileClient { @@ -42,13 +40,26 @@ public interface FileClient {
42 */ 40 */
43 byte[] getContent(String path) throws Exception; 41 byte[] getContent(String path) throws Exception;
44 42
  43 + // ========== 文件签名,目前仅 S3 支持 ==========
  44 +
45 /** 45 /**
46 - * 获得文件预签名地址 46 + * 获得文件预签名地址,用于上传
47 * 47 *
48 * @param path 相对路径 48 * @param path 相对路径
49 * @return 文件预签名地址 49 * @return 文件预签名地址
50 */ 50 */
51 - default FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception { 51 + default String presignPutUrl(String path) {
  52 + throw new UnsupportedOperationException("不支持的操作");
  53 + }
  54 +
  55 + /**
  56 + * 生成文件预签名地址,用于读取
  57 + *
  58 + * @param url 完整的文件访问地址
  59 + * @param expirationSeconds 访问有效期,单位秒
  60 + * @return 文件预签名地址
  61 + */
  62 + default String presignGetUrl(String url, Integer expirationSeconds) {
52 throw new UnsupportedOperationException("不支持的操作"); 63 throw new UnsupportedOperationException("不支持的操作");
53 } 64 }
54 65
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java deleted
1 -package cn.iocoder.yudao.module.infra.framework.file.core.client.s3;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Data;  
5 -import lombok.NoArgsConstructor;  
6 -  
7 -/**  
8 - * 文件预签名地址 Response DTO  
9 - *  
10 - * @author owen  
11 - */  
12 -@Data  
13 -@AllArgsConstructor  
14 -@NoArgsConstructor  
15 -public class FilePresignedUrlRespDTO {  
16 -  
17 - /**  
18 - * 文件上传 URL(用于上传)  
19 - *  
20 - * 例如说:  
21 - */  
22 - private String uploadUrl;  
23 -  
24 - /**  
25 - * 文件 URL(用于读取、下载等)  
26 - */  
27 - private String url;  
28 -  
29 -}  
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java
1 package cn.iocoder.yudao.module.infra.framework.file.core.client.s3; 1 package cn.iocoder.yudao.module.infra.framework.file.core.client.s3;
2 2
3 import cn.hutool.core.io.IoUtil; 3 import cn.hutool.core.io.IoUtil;
  4 +import cn.hutool.core.util.BooleanUtil;
4 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
5 import cn.hutool.http.HttpUtil; 6 import cn.hutool.http.HttpUtil;
  7 +import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
6 import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; 8 import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
7 import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 9 import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
8 import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; 10 import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
@@ -15,9 +17,11 @@ import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; @@ -15,9 +17,11 @@ import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
15 import software.amazon.awssdk.services.s3.model.GetObjectRequest; 17 import software.amazon.awssdk.services.s3.model.GetObjectRequest;
16 import software.amazon.awssdk.services.s3.model.PutObjectRequest; 18 import software.amazon.awssdk.services.s3.model.PutObjectRequest;
17 import software.amazon.awssdk.services.s3.presigner.S3Presigner; 19 import software.amazon.awssdk.services.s3.presigner.S3Presigner;
  20 +import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
18 import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; 21 import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
19 22
20 import java.net.URI; 23 import java.net.URI;
  24 +import java.net.URL;
21 import java.time.Duration; 25 import java.time.Duration;
22 26
23 /** 27 /**
@@ -27,6 +31,8 @@ import java.time.Duration; @@ -27,6 +31,8 @@ import java.time.Duration;
27 */ 31 */
28 public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { 32 public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
29 33
  34 + private static final Duration EXPIRATION_DEFAULT = Duration.ofHours(24);
  35 +
30 private S3Client client; 36 private S3Client client;
31 private S3Presigner presigner; 37 private S3Presigner presigner;
32 38
@@ -75,7 +81,7 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; { @@ -75,7 +81,7 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; {
75 // 上传文件 81 // 上传文件
76 client.putObject(putRequest, RequestBody.fromBytes(content)); 82 client.putObject(putRequest, RequestBody.fromBytes(content));
77 // 拼接返回路径 83 // 拼接返回路径
78 - return config.getDomain() + "/" + path; 84 + return presignGetUrl(path, null);
79 } 85 }
80 86
81 @Override 87 @Override
@@ -97,23 +103,38 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; { @@ -97,23 +103,38 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; {
97 } 103 }
98 104
99 @Override 105 @Override
100 - public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) {  
101 - Duration expiration = Duration.ofHours(24);  
102 - return new FilePresignedUrlRespDTO(getPresignedUrl(path, expiration), config.getDomain() + "/" + path); 106 + public String presignPutUrl(String path) {
  107 + return presigner.presignPutObject(PutObjectPresignRequest.builder()
  108 + .signatureDuration(EXPIRATION_DEFAULT)
  109 + .putObjectRequest(b -> b.bucket(config.getBucket()).key(path)).build())
  110 + .url().toString();
103 } 111 }
104 112
105 - /**  
106 - * 生成动态的预签名上传 URL  
107 - *  
108 - * @param path 相对路径  
109 - * @param expiration 过期时间  
110 - * @return 生成的上传 URL  
111 - */  
112 - private String getPresignedUrl(String path, Duration expiration) {  
113 - return presigner.presignPutObject(PutObjectPresignRequest.builder() 113 + @Override
  114 + public String presignGetUrl(String url, Integer expirationSeconds) {
  115 + // 1. 将 url 转换为 path
  116 + String path = StrUtil.removePrefix(url, config.getDomain() + "/");
  117 + path = HttpUtils.removeUrlQuery(path);
  118 +
  119 + // 2.1 情况一:公开访问:无需签名
  120 + // 考虑到老版本的兼容,所以必须是 config.getEnablePublicAccess() 为 false 时,才进行签名
  121 + if (!BooleanUtil.isFalse(config.getEnablePublicAccess())) {
  122 + return config.getDomain() + "/" + path;
  123 + }
  124 +
  125 + // 2.2 情况二:私有访问:生成 GET 预签名 URL
  126 + String finalPath = path;
  127 + Duration expiration = expirationSeconds != null ? Duration.ofSeconds(expirationSeconds) : EXPIRATION_DEFAULT;
  128 + URL signedUrl = presigner.presignGetObject(GetObjectPresignRequest.builder()
114 .signatureDuration(expiration) 129 .signatureDuration(expiration)
115 - .putObjectRequest(b -> b.bucket(config.getBucket()).key(path))  
116 - .build()).url().toString(); 130 + .getObjectRequest(b -> b.bucket(config.getBucket()).key(finalPath)).build())
  131 + .url();
  132 + // 特殊:适配未使用 domain 返回的情况!!!
  133 + String signedUrlStr = signedUrl.toString();
  134 + if (!signedUrlStr.startsWith(config.getDomain())) {
  135 + signedUrlStr = signedUrlStr.replaceFirst(signedUrl.getProtocol() + "://" + signedUrl.getHost(), config.getDomain());
  136 + }
  137 + return signedUrlStr;
117 } 138 }
118 139
119 /** 140 /**
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java
@@ -73,6 +73,15 @@ public class S3FileClientConfig implements FileClientConfig { @@ -73,6 +73,15 @@ public class S3FileClientConfig implements FileClientConfig {
73 @NotNull(message = "enablePathStyleAccess 不能为空") 73 @NotNull(message = "enablePathStyleAccess 不能为空")
74 private Boolean enablePathStyleAccess; 74 private Boolean enablePathStyleAccess;
75 75
  76 + /**
  77 + * 是否公开访问
  78 + *
  79 + * true:公开访问,所有人都可以访问
  80 + * false:私有访问,只有配置的 accessKey 才可以访问
  81 + */
  82 + @NotNull(message = "是否公开访问不能为空")
  83 + private Boolean enablePublicAccess;
  84 +
76 @SuppressWarnings("RedundantIfStatement") 85 @SuppressWarnings("RedundantIfStatement")
77 @AssertTrue(message = "domain 不能为空") 86 @AssertTrue(message = "domain 不能为空")
78 @JsonIgnore 87 @JsonIgnore
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java
@@ -37,14 +37,22 @@ public interface FileService { @@ -37,14 +37,22 @@ public interface FileService {
37 String name, String directory, String type); 37 String name, String directory, String type);
38 38
39 /** 39 /**
40 - * 生成文件预签名地址信息 40 + * 生成文件预签名地址信息,用于上传
41 * 41 *
42 * @param name 文件名 42 * @param name 文件名
43 * @param directory 目录 43 * @param directory 目录
44 * @return 预签名地址信息 44 * @return 预签名地址信息
45 */ 45 */
46 - FilePresignedUrlRespVO getFilePresignedUrl(@NotEmpty(message = "文件名不能为空") String name,  
47 - String directory); 46 + FilePresignedUrlRespVO presignPutUrl(@NotEmpty(message = "文件名不能为空") String name,
  47 + String directory);
  48 + /**
  49 + * 生成文件预签名地址信息,用于读取
  50 + *
  51 + * @param url 完整的文件访问地址
  52 + * @param expirationSeconds 访问有效期,单位秒
  53 + * @return 文件预签名地址
  54 + */
  55 + String presignGetUrl(String url, Integer expirationSeconds);
48 56
49 /** 57 /**
50 * 创建文件 58 * 创建文件
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java
@@ -6,6 +6,7 @@ import cn.hutool.core.lang.Assert; @@ -6,6 +6,7 @@ import cn.hutool.core.lang.Assert;
6 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
7 import cn.hutool.crypto.digest.DigestUtil; 7 import cn.hutool.crypto.digest.DigestUtil;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO; 11 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
11 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; 12 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;
@@ -13,7 +14,6 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresigned @@ -13,7 +14,6 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresigned
13 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; 14 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
14 import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper; 15 import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper;
15 import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient; 16 import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
16 -import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;  
17 import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils; 17 import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils;
18 import com.google.common.annotations.VisibleForTesting; 18 import com.google.common.annotations.VisibleForTesting;
19 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
@@ -126,19 +126,27 @@ public class FileServiceImpl implements FileService { @@ -126,19 +126,27 @@ public class FileServiceImpl implements FileService {
126 126
127 @Override 127 @Override
128 @SneakyThrows 128 @SneakyThrows
129 - public FilePresignedUrlRespVO getFilePresignedUrl(String name, String directory) { 129 + public FilePresignedUrlRespVO presignPutUrl(String name, String directory) {
130 // 1. 生成上传的 path,需要保证唯一 130 // 1. 生成上传的 path,需要保证唯一
131 String path = generateUploadPath(name, directory); 131 String path = generateUploadPath(name, directory);
132 132
133 // 2. 获取文件预签名地址 133 // 2. 获取文件预签名地址
134 FileClient fileClient = fileConfigService.getMasterFileClient(); 134 FileClient fileClient = fileConfigService.getMasterFileClient();
135 - FilePresignedUrlRespDTO presignedObjectUrl = fileClient.getPresignedObjectUrl(path);  
136 - return BeanUtils.toBean(presignedObjectUrl, FilePresignedUrlRespVO.class,  
137 - object -> object.setConfigId(fileClient.getId()).setPath(path)); 135 + String uploadUrl = fileClient.presignPutUrl(path);
  136 + String visitUrl = fileClient.presignGetUrl(path, null);
  137 + return new FilePresignedUrlRespVO().setConfigId(fileClient.getId())
  138 + .setPath(path).setUploadUrl(uploadUrl).setUrl(visitUrl);
  139 + }
  140 +
  141 + @Override
  142 + public String presignGetUrl(String url, Integer expirationSeconds) {
  143 + FileClient fileClient = fileConfigService.getMasterFileClient();
  144 + return fileClient.presignGetUrl(url, expirationSeconds);
138 } 145 }
139 146
140 @Override 147 @Override
141 public Long createFile(FileCreateReqVO createReqVO) { 148 public Long createFile(FileCreateReqVO createReqVO) {
  149 + createReqVO.setUrl(HttpUtils.removeUrlQuery(createReqVO.getUrl())); // 目的:移除私有桶情况下,URL 的签名参数
142 FileDO file = BeanUtils.toBean(createReqVO, FileDO.class); 150 FileDO file = BeanUtils.toBean(createReqVO, FileDO.class);
143 fileMapper.insert(file); 151 fileMapper.insert(file);
144 return file.getId(); 152 return file.getId();
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java
@@ -9,6 +9,7 @@ import jakarta.validation.Validation; @@ -9,6 +9,7 @@ import jakarta.validation.Validation;
9 import org.junit.jupiter.api.Disabled; 9 import org.junit.jupiter.api.Disabled;
10 import org.junit.jupiter.api.Test; 10 import org.junit.jupiter.api.Test;
11 11
  12 +@SuppressWarnings("resource")
12 public class S3FileClientTest { 13 public class S3FileClientTest {
13 14
14 @Test 15 @Test
@@ -70,6 +71,7 @@ public class S3FileClientTest { @@ -70,6 +71,7 @@ public class S3FileClientTest {
70 config.setAccessSecret("kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP"); 71 config.setAccessSecret("kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP");
71 config.setBucket("ruoyi-vue-pro"); 72 config.setBucket("ruoyi-vue-pro");
72 config.setDomain("http://test.yudao.iocoder.cn"); // 如果有自定义域名,则可以设置。http://static.yudao.iocoder.cn 73 config.setDomain("http://test.yudao.iocoder.cn"); // 如果有自定义域名,则可以设置。http://static.yudao.iocoder.cn
  74 + config.setEnablePathStyleAccess(false);
73 // 默认上海的 endpoint 75 // 默认上海的 endpoint
74 config.setEndpoint("s3-cn-south-1.qiniucs.com"); 76 config.setEndpoint("s3-cn-south-1.qiniucs.com");
75 77
@@ -78,6 +80,32 @@ public class S3FileClientTest { @@ -78,6 +80,32 @@ public class S3FileClientTest {
78 } 80 }
79 81
80 @Test 82 @Test
  83 + @Disabled // 七牛云存储(读私有桶),如果要集成测试,可以注释本行
  84 + public void testQiniu_privateGet() {
  85 + S3FileClientConfig config = new S3FileClientConfig();
  86 + // 配置成你自己的
  87 +// config.setAccessKey(System.getenv("QINIU_ACCESS_KEY"));
  88 +// config.setAccessSecret(System.getenv("QINIU_SECRET_KEY"));
  89 + config.setAccessKey("b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8");
  90 + config.setAccessSecret("kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP");
  91 + config.setBucket("ruoyi-vue-pro-private");
  92 + config.setDomain("http://t151glocd.hn-bkt.clouddn.com"); // 如果有自定义域名,则可以设置。http://static.yudao.iocoder.cn
  93 + config.setEnablePathStyleAccess(false);
  94 + // 默认上海的 endpoint
  95 + config.setEndpoint("s3-cn-south-1.qiniucs.com");
  96 +
  97 + // 校验配置
  98 + ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config);
  99 + // 创建 Client
  100 + S3FileClient client = new S3FileClient(0L, config);
  101 + client.init();
  102 + // 执行生成 URL 签名
  103 + String path = "output.png";
  104 + String presignedUrl = client.presignGetUrl(path, 300);
  105 + System.out.println(presignedUrl);
  106 + }
  107 +
  108 + @Test
81 @Disabled // 华为云存储,如果要集成测试,可以注释本行 109 @Disabled // 华为云存储,如果要集成测试,可以注释本行
82 public void testHuaweiCloud() throws Exception { 110 public void testHuaweiCloud() throws Exception {
83 S3FileClientConfig config = new S3FileClientConfig(); 111 S3FileClientConfig config = new S3FileClientConfig();
@@ -93,7 +121,7 @@ public class S3FileClientTest { @@ -93,7 +121,7 @@ public class S3FileClientTest {
93 testExecuteUpload(config); 121 testExecuteUpload(config);
94 } 122 }
95 123
96 - private void testExecuteUpload(S3FileClientConfig config) throws Exception { 124 + private void testExecuteUpload(S3FileClientConfig config) {
97 // 校验配置 125 // 校验配置
98 ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config); 126 ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config);
99 // 创建 Client 127 // 创建 Client