MinIO
官網:https://min.io
MinIO 是一個基于 Go 實現的高性能、兼容 S3 協議的對象存儲。它采用 GNU AGPL v3 開源協議,項目地址是 https://Github.com/minio/minio 。
它適合存儲海量的非結構化的數據,例如說圖片、音頻、視頻等常見文件,備份數據、容器、虛擬機鏡像等等,小到 1 KB,大到 5 TB 都可以支持。
SpringBoot 整合 Minio
添加依賴
<properties>
<JAVA.version>1.8</java.version>
<minio.version>8.4.3</minio.version>
</properties>
<dependencies>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
</dependencies>
Minio 配置類
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MinioConfig {
@Value("${minio.url}")
private String url;
@Value("${minio.access-key}")
private String accessKey;
@Value("${minio.secret-key}")
private String secretKey;
@Bean
public MinioClient minioClient() {
return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();
}
}
Application.yml 配置文件
minio:
url: http://127.0.0.1:9000
accessKey: 45wsHpkAIWfhghSs11X
secretKey: D9fghfg6sahgufghfgdOYrwqHqocfgh2njhfgh
MinioTemplate.java 封裝方法
封裝常用的上傳(多文件上傳、單文件上傳)、獲取鏈接、刪除、下載方法,方便使用。
import com.ufan.mall.model.FileVo;
import io.minio.*;
import io.minio.http.Method;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:MinioTemplate.java
* @Description:MinioTemplate
* @Author:tanyp
* @Date:2023/07/27 15:49
**/
@Slf4j
@Component
public class MinioTemplate {
@Autowired
private MinioClient client;
/**
* @MonthName:upload
* @Description: 上傳文件
* @Author:tanyp
* @Date:2023/07/27 15:52
* @Param: [file, bucketName]
* @return:void
**/
public FileVo upload(MultipartFile file, String bucketName) {
try {
createBucket(bucketName);
String oldName = file.getOriginalFilename();
String fileName = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + UuidUtil.getRandomPwd(15) + oldName.substring(oldName.lastIndexOf("."));
client.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(file.getInputStream(), file.getSize(), 0)
.contentType(file.getContentType()).build()
);
String url = this.getObjUrl(bucketName, fileName);
return FileVo.builder()
.oldFileName(oldName)
.newFileName(fileName)
.fileUrl(url.substring(0, url.indexOf("?")))
.build();
} catch (Exception e) {
log.error("上傳文件出錯:{}", e);
return null;
}
}
/**
* @MonthName:uploads
* @Description: 上傳多個文件
* @Author:tanyp
* @Date:2023/07/27 15:52
* @Param: [file, bucketName]
* @return:void
**/
public List<FileVo> uploads(List<MultipartFile> files, String bucketName) {
try {
List<FileVo> list = new ArrayList<>();
createBucket(bucketName);
for (MultipartFile file : files) {
String oldName = file.getOriginalFilename();
String fileName = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + UuidUtil.getRandomPwd(15) + oldName.substring(oldName.lastIndexOf("."));
client.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(file.getInputStream(), file.getSize(), 0)
.contentType(file.getContentType()).build()
);
String url = this.getObjUrl(bucketName, fileName);
list.add(
FileVo.builder()
.oldFileName(oldName)
.newFileName(fileName)
.fileUrl(url.substring(0, url.indexOf("?")))
.build()
);
}
return list;
} catch (Exception e) {
log.error("上傳文件出錯:{}", e);
return null;
}
}
/**
* @MonthName:download
* @Description: 下載文件
* @Author:tanyp
* @Date:2023/07/27 15:54
* @Param: [bucketName, fileName]
* @return:void
**/
public void download(String bucketName, String fileName) throws Exception {
client.downloadObject(DownloadObjectArgs.builder().bucket(bucketName).filename(fileName).build());
}
/**
* @MonthName:getObjUrl
* @Description: 獲取文件鏈接
* @Author:tanyp
* @Date:2023/07/27 15:55
* @Param: [bucketName, fileName]
* @return:java.lang.String
**/
public String getObjUrl(String bucketName, String fileName) throws Exception {
return client.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.bucket(bucketName)
.object(fileName)
.method(Method.GET)
.expiry(30, TimeUnit.SECONDS)
.build()
);
}
/**
* @MonthName:delete
* @Description: 刪除文件
* @Author:tanyp
* @Date:2023/5/26 15:56
* @Param: [bucketName, fileName]
* @return:void
**/
public void delete(String bucketName, String fileName) throws Exception {
client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
}
@SneakyThrows
public void createBucket(String bucketName) {
if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
String sb = "{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"AWS": ["*"]},"Action": ["s3:GetBucketLocation"],"Resource": ["arn:aws:s3:::" + bucketName + ""]},{"Effect": "Allow","Principal": {"AWS": ["*"]},"Action": ["s3:ListBucket"],"Resource": ["arn:aws:s3:::" + bucketName + ""],"Condition": {"StringEquals": {"s3:prefix": ["*"]}}},{"Effect": "Allow","Principal": {"AWS": ["*"]},"Action": ["s3:GetObject"],"Resource": ["arn:aws:s3:::" + bucketName + "/**"]}]}";
client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(sb).build());
}
}
}
FileVo.java 實體類
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FileVo {
/**
* 原文件名
*/
private String oldFileName;
/**
* 新文件名
*/
private String newFileName;
/**
* 文件路徑
*/
private String fileUrl;
}
動態創建 Bucket
如何設置桶的權限?
在MinIO中,可以通過設置桶策略來控制桶的訪問權限。桶策略是一個JSON格式的文本文件,用于指定哪些實體(用戶、組或IP地址)可以執行哪些操作(讀、寫、列舉等)。
MinIO桶策略的基本結構如下所示:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": ["action1", "action2", ...],
"Effect": "Allow|Deny",
"Principal": {"AWS": ["arn:aws:iam::account-id:user/user-name"]},
"Resource": ["arn:aws:s3:::bucket-name/object-prefix", ...]
},
...
]
}
備注:
-
• Version:指定策略語法版本(必需)。
-
• Statement:指定一個或多個聲明,每個聲明包含一個或多個條件,用于定義訪問規則。
-
• Action:指定允許或拒絕的操作列表,如"s3:GetObject"表示允許讀取對象。
-
• Effect:指定允許或拒絕操作的結果(必需)。
-
• Principal:指定允許或拒絕操作的主體,如IAM用戶、組或角色。
-
• Resource:指定允許或拒絕操作的資源(必需)。