houzhongyi
2024-07-11 e7c1260db32209a078a962aaa0ad5492c35774fb
提交 | 用户 | 时间
e7c126 1 package com.iailab.module.infra.framework.file.core.client.s3;
H 2
3 import cn.hutool.core.io.IoUtil;
4 import cn.hutool.core.util.StrUtil;
5 import cn.hutool.http.HttpUtil;
6 import com.iailab.module.infra.framework.file.core.client.AbstractFileClient;
7 import io.minio.*;
8 import io.minio.http.Method;
9
10 import java.io.ByteArrayInputStream;
11 import java.util.concurrent.TimeUnit;
12
13 /**
14  * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
15  * <p>
16  * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库
17  *
18  * @author iailab
19  */
20 public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
21
22     private MinioClient client;
23
24     public S3FileClient(Long id, S3FileClientConfig config) {
25         super(id, config);
26     }
27
28     @Override
29     protected void doInit() {
30         // 补全 domain
31         if (StrUtil.isEmpty(config.getDomain())) {
32             config.setDomain(buildDomain());
33         }
34         // 初始化客户端
35         client = MinioClient.builder()
36                 .endpoint(buildEndpointURL()) // Endpoint URL
37                 .region(buildRegion()) // Region
38                 .credentials(config.getAccessKey(), config.getAccessSecret()) // 认证密钥
39                 .build();
40     }
41
42     /**
43      * 基于 endpoint 构建调用云服务的 URL 地址
44      *
45      * @return URI 地址
46      */
47     private String buildEndpointURL() {
48         // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO
49         if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
50             return config.getEndpoint();
51         }
52         return StrUtil.format("https://{}", config.getEndpoint());
53     }
54
55     /**
56      * 基于 bucket + endpoint 构建访问的 Domain 地址
57      *
58      * @return Domain 地址
59      */
60     private String buildDomain() {
61         // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO
62         if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
63             return StrUtil.format("{}/{}", config.getEndpoint(), config.getBucket());
64         }
65         // 阿里云、腾讯云、华为云都适合。七牛云比较特殊,必须有自定义域名
66         return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint());
67     }
68
69     /**
70      * 基于 bucket 构建 region 地区
71      *
72      * @return region 地区
73      */
74     private String buildRegion() {
75         // 阿里云必须有 region,否则会报错
76         if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_ALIYUN)) {
77             return StrUtil.subBefore(config.getEndpoint(), '.', false)
78                     .replaceAll("-internal", "")// 去除内网 Endpoint 的后缀
79                     .replaceAll("https://", "");
80         }
81         // 腾讯云必须有 region,否则会报错
82         if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_TENCENT)) {
83             return StrUtil.subAfter(config.getEndpoint(), "cos.", false)
84                     .replaceAll("." + S3FileClientConfig.ENDPOINT_TENCENT, ""); // 去除 Endpoint
85         }
86         return null;
87     }
88
89     @Override
90     public String upload(byte[] content, String path, String type) throws Exception {
91         // 执行上传
92         client.putObject(PutObjectArgs.builder()
93                 .bucket(config.getBucket()) // bucket 必须传递
94                 .contentType(type)
95                 .object(path) // 相对路径作为 key
96                 .stream(new ByteArrayInputStream(content), content.length, -1) // 文件内容
97                 .build());
98         // 拼接返回路径
99         return config.getDomain() + "/" + path;
100     }
101
102     @Override
103     public void delete(String path) throws Exception {
104         client.removeObject(RemoveObjectArgs.builder()
105                 .bucket(config.getBucket()) // bucket 必须传递
106                 .object(path) // 相对路径作为 key
107                 .build());
108     }
109
110     @Override
111     public byte[] getContent(String path) throws Exception {
112         GetObjectResponse response = client.getObject(GetObjectArgs.builder()
113                 .bucket(config.getBucket()) // bucket 必须传递
114                 .object(path) // 相对路径作为 key
115                 .build());
116         return IoUtil.readBytes(response);
117     }
118
119     @Override
120     public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception {
121         String uploadUrl = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
122                 .method(Method.PUT)
123                 .bucket(config.getBucket())
124                 .object(path)
125                 .expiry(10, TimeUnit.MINUTES) // 过期时间(秒数)取值范围:1 秒 ~ 7 天
126                 .build()
127         );
128         return new FilePresignedUrlRespDTO(uploadUrl, config.getDomain() + "/" + path);
129     }
130
131 }