¶Ô±ÈÐÂÎļþ |
| | |
| | | /* |
| | | * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. |
| | | * |
| | | * Redistribution and use in source and binary forms, with or without |
| | | * modification, are permitted provided that the following conditions are met: |
| | | * |
| | | * Redistributions of source code must retain the above copyright notice, |
| | | * this list of conditions and the following disclaimer. |
| | | * Redistributions in binary form must reproduce the above copyright |
| | | * notice, this list of conditions and the following disclaimer in the |
| | | * documentation and/or other materials provided with the distribution. |
| | | * Neither the name of the dreamlu.net developer nor the names of its |
| | | * contributors may be used to endorse or promote products derived from |
| | | * this software without specific prior written permission. |
| | | * Author: Chill åºéª (smallchill@163.com) |
| | | */ |
| | | package org.springblade.core.oss; |
| | | |
| | | import io.minio.*; |
| | | import io.minio.http.Method; |
| | | import io.minio.messages.Bucket; |
| | | import io.minio.messages.DeleteObject; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.SneakyThrows; |
| | | import org.springblade.core.oss.enums.PolicyType; |
| | | import org.springblade.core.oss.model.BladeFile; |
| | | import org.springblade.core.oss.model.OssFile; |
| | | import org.springblade.core.oss.props.OssProperties; |
| | | import org.springblade.core.oss.rule.OssRule; |
| | | import org.springblade.core.tool.utils.DateUtil; |
| | | import org.springblade.core.tool.utils.Func; |
| | | import org.springblade.core.tool.utils.StringPool; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.InputStream; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | import java.util.stream.Stream; |
| | | |
| | | /** |
| | | * MinIOTemplate |
| | | * |
| | | * @author Chill |
| | | */ |
| | | @AllArgsConstructor |
| | | public class MinioTemplate implements OssTemplate { |
| | | |
| | | /** |
| | | * MinIO客æ·ç«¯ |
| | | */ |
| | | private final MinioClient client; |
| | | |
| | | /** |
| | | * å卿¡¶å½åè§å |
| | | */ |
| | | private final OssRule ossRule; |
| | | |
| | | /** |
| | | * é
置类 |
| | | */ |
| | | private final OssProperties ossProperties; |
| | | |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public void makeBucket(String bucketName) { |
| | | if ( |
| | | !client.bucketExists( |
| | | BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build() |
| | | ) |
| | | ) { |
| | | client.makeBucket( |
| | | MakeBucketArgs.builder().bucket(getBucketName(bucketName)).build() |
| | | ); |
| | | client.setBucketPolicy( |
| | | SetBucketPolicyArgs.builder().bucket(getBucketName(bucketName)).config(getPolicyType(getBucketName(bucketName), PolicyType.READ)).build() |
| | | ); |
| | | } |
| | | } |
| | | |
| | | @SneakyThrows |
| | | public Bucket getBucket() { |
| | | return getBucket(getBucketName()); |
| | | } |
| | | |
| | | @SneakyThrows |
| | | public Bucket getBucket(String bucketName) { |
| | | Optional<Bucket> bucketOptional = client.listBuckets().stream().filter(bucket -> bucket.name().equals(getBucketName(bucketName))).findFirst(); |
| | | return bucketOptional.orElse(null); |
| | | } |
| | | |
| | | @SneakyThrows |
| | | public List<Bucket> listBuckets() { |
| | | return client.listBuckets(); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public void removeBucket(String bucketName) { |
| | | client.removeBucket( |
| | | RemoveBucketArgs.builder().bucket(getBucketName(bucketName)).build() |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public boolean bucketExists(String bucketName) { |
| | | return client.bucketExists( |
| | | BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build() |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public void copyFile(String bucketName, String fileName, String destBucketName) { |
| | | copyFile(bucketName, fileName, destBucketName, fileName); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) { |
| | | client.copyObject( |
| | | CopyObjectArgs.builder() |
| | | .source(CopySource.builder().bucket(getBucketName(bucketName)).object(fileName).build()) |
| | | .bucket(getBucketName(destBucketName)) |
| | | .object(destFileName) |
| | | .build() |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public OssFile statFile(String fileName) { |
| | | return statFile(ossProperties.getBucketName(), fileName); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public OssFile statFile(String bucketName, String fileName) { |
| | | StatObjectResponse stat = client.statObject( |
| | | StatObjectArgs.builder().bucket(getBucketName(bucketName)).object(fileName).build() |
| | | ); |
| | | OssFile ossFile = new OssFile(); |
| | | ossFile.setName(Func.isEmpty(stat.object()) ? fileName : stat.object()); |
| | | ossFile.setLink(fileLink(ossFile.getName())); |
| | | ossFile.setHash(String.valueOf(stat.hashCode())); |
| | | ossFile.setLength(stat.size()); |
| | | ossFile.setPutTime(DateUtil.toDate(stat.lastModified().toLocalDateTime())); |
| | | ossFile.setContentType(stat.contentType()); |
| | | return ossFile; |
| | | } |
| | | |
| | | @Override |
| | | public String filePath(String fileName) { |
| | | return getBucketName().concat(StringPool.SLASH).concat(fileName); |
| | | } |
| | | |
| | | @Override |
| | | public String filePath(String bucketName, String fileName) { |
| | | return getBucketName(bucketName).concat(StringPool.SLASH).concat(fileName); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public String fileLink(String fileName) { |
| | | return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(getBucketName()).concat(StringPool.SLASH).concat(fileName); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public String fileLink(String bucketName, String fileName) { |
| | | return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(getBucketName(bucketName)).concat(StringPool.SLASH).concat(fileName); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public BladeFile putFile(MultipartFile file) { |
| | | return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public BladeFile putFile(String fileName, MultipartFile file) { |
| | | return putFile(ossProperties.getBucketName(), fileName, file); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public BladeFile putFile(String bucketName, String fileName, MultipartFile file) { |
| | | return putFile(bucketName, file.getOriginalFilename(), file.getInputStream()); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public BladeFile putFile(String fileName, InputStream stream) { |
| | | return putFile(ossProperties.getBucketName(), fileName, stream); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public BladeFile putFile(String bucketName, String fileName, InputStream stream) { |
| | | return putFile(bucketName, fileName, stream, "application/octet-stream"); |
| | | } |
| | | |
| | | @SneakyThrows |
| | | public BladeFile putFile(String bucketName, String fileName, InputStream stream, String contentType) { |
| | | makeBucket(bucketName); |
| | | String originalName = fileName; |
| | | fileName = getFileName(fileName); |
| | | client.putObject( |
| | | PutObjectArgs.builder() |
| | | .bucket(getBucketName(bucketName)) |
| | | .object(fileName) |
| | | .stream(stream, stream.available(), -1) |
| | | .contentType(contentType) |
| | | .build() |
| | | ); |
| | | BladeFile file = new BladeFile(); |
| | | file.setOriginalName(originalName); |
| | | file.setName(fileName); |
| | | file.setDomain(getOssHost(bucketName)); |
| | | file.setLink(fileLink(bucketName, fileName)); |
| | | return file; |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public void removeFile(String fileName) { |
| | | removeFile(ossProperties.getBucketName(), fileName); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public void removeFile(String bucketName, String fileName) { |
| | | client.removeObject( |
| | | RemoveObjectArgs.builder().bucket(getBucketName(bucketName)).object(fileName).build() |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public void removeFiles(List<String> fileNames) { |
| | | removeFiles(ossProperties.getBucketName(), fileNames); |
| | | } |
| | | |
| | | @Override |
| | | @SneakyThrows |
| | | public void removeFiles(String bucketName, List<String> fileNames) { |
| | | Stream<DeleteObject> stream = fileNames.stream().map(DeleteObject::new); |
| | | client.removeObjects(RemoveObjectsArgs.builder().bucket(getBucketName(bucketName)).objects(stream::iterator).build()); |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®è§åçæå卿¡¶åç§°è§å |
| | | * |
| | | * @return String |
| | | */ |
| | | private String getBucketName() { |
| | | return getBucketName(ossProperties.getBucketName()); |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®è§åçæå卿¡¶åç§°è§å |
| | | * |
| | | * @param bucketName å卿¡¶åç§° |
| | | * @return String |
| | | */ |
| | | private String getBucketName(String bucketName) { |
| | | return ossRule.bucketName(bucketName); |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®è§åçææä»¶åç§°è§å |
| | | * |
| | | * @param originalFilename åå§æä»¶å |
| | | * @return string |
| | | */ |
| | | private String getFileName(String originalFilename) { |
| | | return ossRule.fileName(originalFilename); |
| | | } |
| | | |
| | | /** |
| | | * è·åæä»¶å¤é¾ |
| | | * |
| | | * @param bucketName bucketåç§° |
| | | * @param fileName æä»¶åç§° |
| | | * @param expires è¿ææ¶é´ <=7 ç§çº§ |
| | | * @return url |
| | | */ |
| | | @SneakyThrows |
| | | public String getPresignedObjectUrl(String bucketName, String fileName, Integer expires) { |
| | | return client.getPresignedObjectUrl( |
| | | GetPresignedObjectUrlArgs.builder() |
| | | .method(Method.GET) |
| | | .bucket(getBucketName(bucketName)) |
| | | .object(fileName) |
| | | .expiry(expires) |
| | | .build() |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * è·åå卿¡¶çç¥ |
| | | * |
| | | * @param policyType çç¥æä¸¾ |
| | | * @return String |
| | | */ |
| | | public String getPolicyType(PolicyType policyType) { |
| | | return getPolicyType(getBucketName(), policyType); |
| | | } |
| | | |
| | | /** |
| | | * è·åå卿¡¶çç¥ |
| | | * |
| | | * @param bucketName å卿¡¶åç§° |
| | | * @param policyType çç¥æä¸¾ |
| | | * @return String |
| | | */ |
| | | public static String getPolicyType(String bucketName, PolicyType policyType) { |
| | | StringBuilder builder = new StringBuilder(); |
| | | builder.append("{\n"); |
| | | builder.append(" \"Statement\": [\n"); |
| | | builder.append(" {\n"); |
| | | builder.append(" \"Action\": [\n"); |
| | | |
| | | switch (policyType) { |
| | | case WRITE: |
| | | builder.append(" \"s3:GetBucketLocation\",\n"); |
| | | builder.append(" \"s3:ListBucketMultipartUploads\"\n"); |
| | | break; |
| | | case READ_WRITE: |
| | | builder.append(" \"s3:GetBucketLocation\",\n"); |
| | | builder.append(" \"s3:ListBucket\",\n"); |
| | | builder.append(" \"s3:ListBucketMultipartUploads\"\n"); |
| | | break; |
| | | default: |
| | | builder.append(" \"s3:GetBucketLocation\"\n"); |
| | | break; |
| | | } |
| | | |
| | | builder.append(" ],\n"); |
| | | builder.append(" \"Effect\": \"Allow\",\n"); |
| | | builder.append(" \"Principal\": \"*\",\n"); |
| | | builder.append(" \"Resource\": \"arn:aws:s3:::"); |
| | | builder.append(bucketName); |
| | | builder.append("\"\n"); |
| | | builder.append(" },\n"); |
| | | if (PolicyType.READ.equals(policyType)) { |
| | | builder.append(" {\n"); |
| | | builder.append(" \"Action\": [\n"); |
| | | builder.append(" \"s3:ListBucket\"\n"); |
| | | builder.append(" ],\n"); |
| | | builder.append(" \"Effect\": \"Deny\",\n"); |
| | | builder.append(" \"Principal\": \"*\",\n"); |
| | | builder.append(" \"Resource\": \"arn:aws:s3:::"); |
| | | builder.append(bucketName); |
| | | builder.append("\"\n"); |
| | | builder.append(" },\n"); |
| | | |
| | | } |
| | | builder.append(" {\n"); |
| | | builder.append(" \"Action\": "); |
| | | |
| | | switch (policyType) { |
| | | case WRITE: |
| | | builder.append("[\n"); |
| | | builder.append(" \"s3:AbortMultipartUpload\",\n"); |
| | | builder.append(" \"s3:DeleteObject\",\n"); |
| | | builder.append(" \"s3:ListMultipartUploadParts\",\n"); |
| | | builder.append(" \"s3:PutObject\"\n"); |
| | | builder.append(" ],\n"); |
| | | break; |
| | | case READ_WRITE: |
| | | builder.append("[\n"); |
| | | builder.append(" \"s3:AbortMultipartUpload\",\n"); |
| | | builder.append(" \"s3:DeleteObject\",\n"); |
| | | builder.append(" \"s3:GetObject\",\n"); |
| | | builder.append(" \"s3:ListMultipartUploadParts\",\n"); |
| | | builder.append(" \"s3:PutObject\"\n"); |
| | | builder.append(" ],\n"); |
| | | break; |
| | | default: |
| | | builder.append("\"s3:GetObject\",\n"); |
| | | break; |
| | | } |
| | | |
| | | builder.append(" \"Effect\": \"Allow\",\n"); |
| | | builder.append(" \"Principal\": \"*\",\n"); |
| | | builder.append(" \"Resource\": \"arn:aws:s3:::"); |
| | | builder.append(bucketName); |
| | | builder.append("/*\"\n"); |
| | | builder.append(" }\n"); |
| | | builder.append(" ],\n"); |
| | | builder.append(" \"Version\": \"2012-10-17\"\n"); |
| | | builder.append("}\n"); |
| | | return builder.toString(); |
| | | } |
| | | |
| | | /** |
| | | * è·ååå |
| | | * |
| | | * @param bucketName å卿¡¶åç§° |
| | | * @return String |
| | | */ |
| | | public String getOssHost(String bucketName) { |
| | | return ossProperties.getEndpoint() + StringPool.SLASH + getBucketName(bucketName); |
| | | } |
| | | |
| | | /** |
| | | * è·ååå |
| | | * |
| | | * @return String |
| | | */ |
| | | public String getOssHost() { |
| | | return getOssHost(ossProperties.getBucketName()); |
| | | } |
| | | |
| | | } |