xm
2024-06-14 722af26bc6fec32bb289b1df51a9016a4935610f
提交 | 用户 | 时间
722af2 1 package com.dl.oss.core;
X 2
3 import cn.hutool.core.io.IoUtil;
4 import cn.hutool.core.util.IdUtil;
5 import com.amazonaws.ClientConfiguration;
6 import com.amazonaws.HttpMethod;
7 import com.amazonaws.Protocol;
8 import com.amazonaws.auth.AWSCredentials;
9 import com.amazonaws.auth.AWSCredentialsProvider;
10 import com.amazonaws.auth.AWSStaticCredentialsProvider;
11 import com.amazonaws.auth.BasicAWSCredentials;
12 import com.amazonaws.client.builder.AwsClientBuilder;
13 import com.amazonaws.services.s3.AmazonS3;
14 import com.amazonaws.services.s3.AmazonS3Client;
15 import com.amazonaws.services.s3.AmazonS3ClientBuilder;
16 import com.amazonaws.services.s3.model.*;
17 import com.dl.common.utils.DateUtils;
18 import com.dl.common.utils.StringUtils;
19 import com.dl.oss.constant.OssConstant;
20 import com.dl.oss.entity.UploadResult;
21 import com.dl.oss.enumd.AccessPolicyType;
22 import com.dl.oss.enumd.PolicyType;
23 import com.dl.oss.exception.OssException;
24 import com.dl.oss.properties.OssProperties;
25
26 import java.io.ByteArrayInputStream;
27 import java.io.InputStream;
28 import java.net.URL;
29 import java.util.Date;
30
31 /**
32  * S3 存储协议 所有兼容S3协议的云厂商均支持
33  * 阿里云 腾讯云 七牛云 minio
34  *
35  * @author Lion Li
36  */
37 public class OssClient {
38
39     private final String configKey;
40
41     private final OssProperties properties;
42
43     private final AmazonS3 client;
44
45     public OssClient(String configKey, OssProperties ossProperties) {
46         this.configKey = configKey;
47         this.properties = ossProperties;
48         try {
49             AwsClientBuilder.EndpointConfiguration endpointConfig =
50                 new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion());
51
52             AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey());
53             AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
54             ClientConfiguration clientConfig = new ClientConfiguration();
55             if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) {
56                 clientConfig.setProtocol(Protocol.HTTPS);
57             } else {
58                 clientConfig.setProtocol(Protocol.HTTP);
59             }
60             AmazonS3ClientBuilder build = AmazonS3Client.builder()
61                 .withEndpointConfiguration(endpointConfig)
62                 .withClientConfiguration(clientConfig)
63                 .withCredentials(credentialsProvider)
64                 .disableChunkedEncoding();
65             if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) {
66                 // minio 使用https限制使用域名访问 需要此配置 站点填域名
67                 build.enablePathStyleAccess();
68             }
69             this.client = build.build();
70
71             createBucket();
72         } catch (Exception e) {
73             if (e instanceof OssException) {
74                 throw e;
75             }
76             throw new OssException("配置错误! 请检查系统配置:[" + e.getMessage() + "]");
77         }
78     }
79
80     public void createBucket() {
81         try {
82             String bucketName = properties.getBucketName();
83             if (client.doesBucketExistV2(bucketName)) {
84                 return;
85             }
86             CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
87             AccessPolicyType accessPolicy = getAccessPolicy();
88             createBucketRequest.setCannedAcl(accessPolicy.getAcl());
89             client.createBucket(createBucketRequest);
90             client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType()));
91         } catch (Exception e) {
92             throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]");
93         }
94     }
95
96     public UploadResult upload(byte[] data, String path, String contentType) {
97         return upload(new ByteArrayInputStream(data), path, contentType);
98     }
99
100     public UploadResult upload(InputStream inputStream, String path, String contentType) {
101         if (!(inputStream instanceof ByteArrayInputStream)) {
102             inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
103         }
104         try {
105             ObjectMetadata metadata = new ObjectMetadata();
106             metadata.setContentType(contentType);
107             metadata.setContentLength(inputStream.available());
108             PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata);
109             // 设置上传对象的 Acl 为公共读
110             putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
111             client.putObject(putObjectRequest);
112         } catch (Exception e) {
113             throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]");
114         }
115         return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
116     }
117
118     public void delete(String path) {
119         path = path.replace(getUrl() + "/", "");
120         try {
121             client.deleteObject(properties.getBucketName(), path);
122         } catch (Exception e) {
123             throw new OssException("删除文件失败,请检查配置信息:[" + e.getMessage() + "]");
124         }
125     }
126
127     public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
128         return upload(data, getPath(properties.getPrefix(), suffix), contentType);
129     }
130
131     public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
132         return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
133     }
134
135     /**
136      * 获取文件元数据
137      *
138      * @param path 完整文件路径
139      */
140     public ObjectMetadata getObjectMetadata(String path) {
141         path = path.replace(getUrl() + "/", "");
142         S3Object object = client.getObject(properties.getBucketName(), path);
143         return object.getObjectMetadata();
144     }
145
146     public InputStream getObjectContent(String path) {
147         path = path.replace(getUrl() + "/", "");
148         S3Object object = client.getObject(properties.getBucketName(), path);
149         return object.getObjectContent();
150     }
151
152     public String getUrl() {
153         String domain = properties.getDomain();
154         String endpoint = properties.getEndpoint();
155         String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://";
156         // 云服务商直接返回
157         if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
158             if (StringUtils.isNotBlank(domain)) {
159                 return header + domain;
160             }
161             return header + properties.getBucketName() + "." + endpoint;
162         }
163         // minio 单独处理
164         if (StringUtils.isNotBlank(domain)) {
165             return header + domain + "/" + properties.getBucketName();
166         }
167         return header + endpoint + "/" + properties.getBucketName();
168     }
169
170     public String getPath(String prefix, String suffix) {
171         // 生成uuid
172         String uuid = IdUtil.fastSimpleUUID();
173         // 文件路径
174         String path = DateUtils.datePath() + "/" + uuid;
175         if (StringUtils.isNotBlank(prefix)) {
176             path = prefix + "/" + path;
177         }
178         return path + suffix;
179     }
180
181
182     public String getConfigKey() {
183         return configKey;
184     }
185
186     /**
187      * 获取私有URL链接
188      *
189      * @param objectKey 对象KEY
190      * @param second    授权时间
191      */
192     public String getPrivateUrl(String objectKey, Integer second) {
193         GeneratePresignedUrlRequest generatePresignedUrlRequest =
194             new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)
195                 .withMethod(HttpMethod.GET)
196                 .withExpiration(new Date(System.currentTimeMillis() + 1000L * second));
197         URL url = client.generatePresignedUrl(generatePresignedUrlRequest);
198         return url.toString();
199     }
200
201     /**
202      * 检查配置是否相同
203      */
204     public boolean checkPropertiesSame(OssProperties properties) {
205         return this.properties.equals(properties);
206     }
207
208     /**
209      * 获取当前桶权限类型
210      *
211      * @return 当前桶权限类型code
212      */
213     public AccessPolicyType getAccessPolicy() {
214         return AccessPolicyType.getByType(properties.getAccessPolicy());
215     }
216
217     private static String getPolicy(String bucketName, PolicyType policyType) {
218         StringBuilder builder = new StringBuilder();
219         builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n");
220         if (policyType == PolicyType.WRITE) {
221             builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n");
222         } else if (policyType == PolicyType.READ_WRITE) {
223             builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n");
224         } else {
225             builder.append("\"s3:GetBucketLocation\"\n");
226         }
227         builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
228         builder.append(bucketName);
229         builder.append("\"\n},\n");
230         if (policyType == PolicyType.READ) {
231             builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
232             builder.append(bucketName);
233             builder.append("\"\n},\n");
234         }
235         builder.append("{\n\"Action\": ");
236         switch (policyType) {
237             case WRITE:
238                 builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n");
239                 break;
240             case READ_WRITE:
241                 builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n");
242                 break;
243             default:
244                 builder.append("\"s3:GetObject\",\n");
245                 break;
246         }
247         builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
248         builder.append(bucketName);
249         builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n");
250         return builder.toString();
251     }
252
253 }