1. 簡介
MinIO 是一個基於Apache License v2.0開源協議的物件儲存服務。它相容亞馬遜S3雲端儲存服務介面,非常適合於儲存大容量非結構化的資料,例如圖片、視訊、日誌檔案、備份資料和容器/虛擬機器映象等,而一個物件檔案可以是任意大小,從幾kb到最大5T不等。
MinIO是一個非常輕量的服務,可以很簡單的和其他應用的結合,類似 NodeJS, Redis 或者 MySQL。
Minio使用糾刪碼erasure code
和校驗和checksum
來保護資料免受硬體故障和無聲資料損壞。 即便您丟失一半數量(N/2)的硬碟,您仍然可以恢復資料。
想要使資料丟失可恢復, 最少得掛載4塊盤或4的倍數。
1.1 什麼是糾刪碼erasure code
?
糾刪碼是一種恢復丟失和損壞資料的數學演算法, Minio採用Reed-Solomon code將物件拆分成N/2資料和N/2 奇偶校驗塊。 這就意味著如果是12塊盤,一個物件會被分成6個資料塊、6個奇偶校驗塊,你可以丟失任意6塊盤(不管其是存放的資料塊還是奇偶校驗塊),你仍可以從剩下的盤中的資料進行恢復。
其中原資料塊和校驗塊是根據儲存型別決定的。儲存塊設定,預設是原資料塊的一半。
1.2 為什麼糾刪碼有用?
糾刪碼的工作原理和RAID或者複製不同,像RAID6可以在損失兩塊盤的情況下不丟資料,而Minio糾刪碼可以在丟失一半的盤的情況下,仍可以保證資料安全。 而且Minio糾刪碼是作用在物件級別,可以一次恢復一個物件,而RAID是作用在卷級別,資料恢復時間很長。 Minio對每個物件單獨編碼,儲存服務一經部署,通常情況下是不需要更換硬碟或者修復。Minio糾刪碼的設計目標是為了效能和儘可能的使用硬體加速。
2. 安裝
2.1 docker
MinIO自定義Access和Secret金鑰
要覆蓋MinIO的自動生成的金鑰,可以將Access和Secret金鑰設為環境變數。 MinIO允許常規字串作為Access和Secret金鑰。
docker run -p 9000:9000 --name minio1 \
-e "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" \
-e "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
-v /mnt/data:/data \
-v /mnt/config:/root/.minio \
minio/minio server /data
2.2 docker-compose
version: '3.0'
services:
minio:
image: minio/minio
container_name: minio
ports:
- "9000:9000"
restart: always
command: server /data
environment:
MINIO_ACCESS_KEY: admin
#大於等於8位
MINIO_SECRET_KEY: admin123
volumes:
- /Users/ludangxin/usr/local/docker/minio/data:/data # 對映檔案路徑
2.3 單機多磁碟掛載
掛載多個冗餘盤,防止資料丟失。
docker run -p 9000:9000 --name minio1 \
-e "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" \
-e "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
-v /mnt/data:/data1 \
-v /mnt/data:/data2 \
-v /mnt/data:/data3 \
-v /mnt/data:/data4 \
-v /mnt/config:/root/.minio \
minio/minio server /data1 /data2 /data3 /data4
2.4 分散式部署
啟動一個分散式Minio例項,你只需要把硬碟位置做為引數傳給minio server命令即可,然後,你需要在所有其它節點執行同樣的命令。
注意
- 分散式Minio裡所有的節點需要有同樣的access祕鑰和secret祕鑰,這樣這些節點才能建立聯接。為了實現這個,你需要在執行minio server命令之前,先將access祕鑰和secret祕鑰export成環境變數。
- 分散式Minio使用的磁碟裡必須是乾淨的,裡面沒有資料。
- 下面示例裡的IP僅供示例參考,你需要改成你真實用到的IP和資料夾路徑。
- 分散式Minio裡的節點時間差不能超過3秒,你可以使用NTP 來保證時間一致。
- 在Windows下執行分散式Minio處於實驗階段,請悠著點使用。
2.4.1 GNU/Linux 和 macOS
export MINIO_ACCESS_KEY=<ACCESS_KEY>
export MINIO_SECRET_KEY=<SECRET_KEY>
minio server http://192.168.1.11/export1 http://192.168.1.12/export2 \
http://192.168.1.13/export3 http://192.168.1.14/export4 \
http://192.168.1.15/export5 http://192.168.1.16/export6 \
http://192.168.1.17/export7 http://192.168.1.18/export8
2.4.2 docker-compose
部署4個節點,每個節點掛載一個盤。
version: '3.7'
services:
minio:
image: minio/minio
volumes:
# 值需要換成對應節點的
- /data0:/data0
ports:
- 9000:9000
environment:
MINIO_ACCESS_KEY: admin
MINIO_SECRET_KEY: admin123
command: minio server http://minio-00/data0 http://minio-01/data1 http://minio-02/data2 http://minio-03/data3
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
# 指定當前節點的hostname
hostname: minio-00
# ip地址要換成自己的
extra_hosts:
- "minio-00:192.168.1.11"
- "minio-01:192.168.1.12"
- "minio-02:192.168.1.13"
- "minio-03:192.168.1.14"
部署4個節點,每個節點四個盤。
version: '3.7'
services:
minio:
image: minio/minio
volumes:
- /data0:/data0
- /data1:/data1
- /data2:/data2
- /data3:/data3
ports:
- 9000:9000
environment:
MINIO_ACCESS_KEY: admin
MINIO_SECRET_KEY: admin123
command: minio server http://minio-0{0...3}/data{0...3}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
# 指定當前節點的hostname
hostname: minio-00
# ip地址要換成自己的
extra_hosts:
- "minio-00:192.168.1.11"
- "minio-01:192.168.1.12"
- "minio-02:192.168.1.13"
- "minio-03:192.168.1.14"
2.5 訪問測試
如圖:訪問localhost:9000
,即可訪問minio 登陸頁面。
3. quick start
3.1 專案結構
├── pom.xml
└── src
└── main
├── java
│ └── com
│ └── ldx
│ └── minio
│ ├── MinioApplication.java # 啟動類
│ ├── config
│ │ ├── MinioConfig.java # minio 配置類
│ │ └── MinioProperties.java # minio 服務引數
│ ├── controller
│ │ └── MinioController.java # 測試控制器
│ ├── test
│ │ └── FileUploader.java # 測試上傳
│ └── util
│ └── MinioUtils.java # 工具類
└── resources
└── application.yaml # 服務配置信檔案
3.2 引入依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ldx</groupId>
<artifactId>minio</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>minio</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- springweb 支援 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- minio 工具包 -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.0.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.2 上傳測試
我們先利用測試程式碼測試一下 minio 附件上傳功能。測試程式碼如下:
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import lombok.extern.slf4j.Slf4j;
import io.minio.MinioClient;
import io.minio.errors.MinioException;
@Slf4j
public class FileUploader {
public static void main(String[] args) throws NoSuchAlgorithmException, IOException, InvalidKeyException {
try {
// 使用MinIO服務的URL,埠,Access key和Secret key建立一個MinioClient物件
MinioClient minioClient = new MinioClient("http://localhost:9000", "admin", "admin123");
// 檢查儲存桶是否已經存在
String bucketName = "test";
boolean isExist = minioClient.bucketExists(bucketName);
if(isExist) {
log.info("Bucket already exists.");
} else {
// 建立一個名為test的儲存桶,用於儲存照片的zip檔案。
minioClient.makeBucket(bucketName);
}
// 使用putObject上傳一個檔案到儲存桶中。
minioClient.putObject(bucketName,"lion.jpg", "/Users/ludangxin/temp/舞獅.png",null);
log.info("/Users/ludangxin/temp/舞獅.png is successfully uploaded as lion.jpg to `test` bucket.");
} catch(MinioException e) {
log.info("Error occurred: " + e);
}
}
}
直接執行mian方法,執行成功後檢視minio ui介面如下。圖片上傳成功,so easy~
3.3 application.yaml
minio:
endpoint: http://localhost
port: 9000
default-bucket-name: ldx
access-key: admin
secret-key: admin123
3.4 MinioProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* minio 配置屬性類
*
* @author ludangxin
* @date 2021/8/17
*/
@Data
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
/**
* 端點
*/
private String endpoint;
/**
* 埠
*/
private Integer port;
/**
* 預設的桶名稱
*/
private String defaultBucketName;
/**
* 訪問key
*/
private String accessKey;
/**
* 金鑰
*/
private String secretKey;
}
3.5 MinioConfig
import io.minio.MinioClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Objects;
/**
* minio 配置類
*
* @author ludangxin
* @date 2021/8/17
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfig {
@Bean
@SneakyThrows
public MinioClient minioClient(MinioProperties minioProperties) {
MinioClient minioClient = new MinioClient(minioProperties.getEndpoint(), minioProperties.getPort(),
minioProperties.getAccessKey(), minioProperties.getSecretKey());
String defaultBucketName = minioProperties.getDefaultBucketName();
if(Objects.nonNull(defaultBucketName)) {
// 建立預設的bucket
if(!minioClient.bucketExists(defaultBucketName)) {
log.info("create default bucket \"{}\" success", defaultBucketName);
minioClient.makeBucket(defaultBucketName);
}
}
return minioClient;
}
}
3.6 MinioController
import com.ldx.minio.util.MinioUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* minio 控制器
*
* @author ludangxin
* @date 2021/8/18
*/
@RestController
@RequestMapping("minio")
@RequiredArgsConstructor
public class MinioController {
private final MinioUtils minioUtils;
@GetMapping
public String getUrl(String fileName) {
return minioUtils.getFileUrl(fileName);
}
@PostMapping
public String upload(MultipartFile file) {
return minioUtils.upload(file);
}
@GetMapping("download/{fileName}")
public void download(@PathVariable String fileName, HttpServletResponse response) {
minioUtils.download(fileName, response);
}
@DeleteMapping
public void del(String fileName) {
minioUtils.delFile(fileName);
}
}
3.7 MinioUtils
package com.ldx.minio.util;
import io.minio.MinioClient;
import io.minio.PutObjectOptions;
import io.minio.messages.Bucket;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
/**
* minio 工具類
*
* @author ludangxin
* @date 2021/8/17
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class MinioUtils {
@Value("${minio.default-bucket-name}")
private String defaultBucketName;
private final MinioClient minioClient;
/**
* 獲取全部bucket
*
* @return all bucket
*/
@SneakyThrows
public List<Bucket> getAllBuckets() {
return minioClient.listBuckets();
}
/**
* 判斷 bucket是否存在
*
* @param bucketName 桶名稱
* @return true 存在
*/
@SneakyThrows
public boolean bucketExists(String bucketName){
return minioClient.bucketExists(bucketName);
}
/**
* 建立 bucket
*
* @param bucketName 桶名稱
*/
@SneakyThrows
public void createBucket(String bucketName){
boolean isExist = minioClient.bucketExists(bucketName);
if(!isExist) {
minioClient.makeBucket(bucketName);
}
}
/**
* 檔案上傳
*
* @param bucketName 桶名稱
* @param fileName 上傳後的檔名稱
* @param fileAbsolutePath 檔案的絕對路徑
* @return 檔案url
*/
@SneakyThrows
public String upload(String bucketName, String fileName, String fileAbsolutePath){
minioClient.putObject(bucketName, fileName, fileAbsolutePath,null);
return getFileUrl(bucketName, fileName);
}
/**
* 檔案上傳
*
* @param fileName 上傳後的檔名稱
* @param stream 檔案輸入流
* @return 檔案url
*/
@SneakyThrows
public String upload(String fileName, InputStream stream){
this.upload(defaultBucketName, fileName, stream);
return getFileUrl(defaultBucketName, fileName);
}
/**
* 檔案上傳
*
* @param file 檔案
* @return 檔案url
*/
public String upload(MultipartFile file) {
final String fileName = file.getOriginalFilename();
this.upload(defaultBucketName, file);
return this.getFileUrl(defaultBucketName, fileName);
}
/**
* 檔案上傳
*
* @param bucketName 桶名稱
* @param file 檔案
* @return 檔案url
*/
public String upload(String bucketName, MultipartFile file) {
InputStream is = null;
try {
is = file.getInputStream();
final String fileName = file.getOriginalFilename();
this.upload(bucketName, fileName, is);
return getFileUrl(bucketName, fileName);
}
catch(Exception e) {
log.error(e.getMessage());
}
finally {
try {
if(Objects.nonNull(is)) {
is.close();
}
} catch(IOException e) {
log.error(e.getMessage());
}
}
return null;
}
/**
* 檔案上傳
*
* @param bucketName 桶名稱
* @param fileName 上傳後的檔名稱
* @param stream 檔案輸入流
* @return 檔案url
*/
@SneakyThrows
public String upload(String bucketName, String fileName, InputStream stream){
minioClient.putObject(bucketName, fileName, stream, new PutObjectOptions(stream.available(), -1));
return getFileUrl(bucketName, fileName);
}
/**
* 附件下載
*
* @param fileName 附件名稱
*/
@SneakyThrows
public void download(String fileName, HttpServletResponse response) {
this.download(defaultBucketName, fileName, response);
}
/**
* 檔案下載
*
* @param bucketName 桶名稱
* @param fileName 檔名稱
*/
public void download(String bucketName, String fileName, HttpServletResponse response) {
InputStream in = null;
OutputStream out = null;
try {
in = minioClient.getObject(bucketName, fileName);
int len = 0;
byte[] buffer = new byte[1024];
out = response.getOutputStream();
response.reset();
response.addHeader("Content-Disposition",
" attachment;filename=" + new String(fileName.getBytes(), StandardCharsets.ISO_8859_1));
response.setContentType("application/octet-stream");
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} catch (Exception e) {
log.error(e.getMessage());
} finally {
if (in != null){
try {
in.close();
} catch (Exception e) {
log.error(e.getMessage());
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
}
/**
* 刪除檔案
*
* @param fileName 檔名稱
*/
@SneakyThrows
public void delFile(String fileName){
this.delFile(defaultBucketName, fileName);
}
/**
* 刪除檔案
*
* @param bucketName 桶名稱
* @param fileName 檔名稱
*/
@SneakyThrows
public void delFile(String bucketName, String fileName){
minioClient.removeObject(bucketName, fileName);
}
/**
* 獲取minio檔案的下載地址
*
* @param fileName 檔名
*/
@SneakyThrows
public String getFileUrl(String fileName) {
return this.getFileUrl(defaultBucketName, fileName);
}
/**
* 獲取minio檔案的下載地址
*
* @param bucketName 桶名稱
* @param fileName 檔名
*/
@SneakyThrows
public String getFileUrl(String bucketName, String fileName) {
return minioClient.presignedGetObject(bucketName, fileName);
}
/**
* 設定桶策略
*
* @param bucketName 桶名稱
* @param policy 策略
*/
@SneakyThrows
public void setBucketPolicy(String bucketName, String policy) {
minioClient.setBucketPolicy(bucketName, policy);
}
}
4. 最新版本工具包
在當前最新的jar中(8.3.0
),minio 工具類的使用方法跟7.0.2
版本還是有很大出入的,在這裡記錄一下。
4.1 MinioConfig
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Objects;
/**
* minio 配置類
*
* @author ludangxin
* @date 2021/8/17
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfig {
@Bean
@SneakyThrows
public MinioClient minioClient(MinioProperties minioProperties) {
MinioClient minioClient = MinioClient.builder()
.endpoint(minioProperties.getEndpoint(), minioProperties.getPort(), false)
.credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
.build();
String defaultBucketName = minioProperties.getDefaultBucketName();
if(Objects.nonNull(defaultBucketName)) {
BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder()
.bucket(defaultBucketName)
.build();
// 建立預設的bucket
if(!minioClient.bucketExists(bucketExistsArgs)) {
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder()
.bucket(defaultBucketName)
.build();
minioClient.makeBucket(makeBucketArgs);
log.info("create default bucket \"{}\" success", defaultBucketName);
}
}
return minioClient;
}
}
4.2 MinioUtils
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* minio 工具類
*
* @author ludangxin
* @date 2021/8/17
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class MinioUtils {
@Value("${minio.default-bucket-name}")
private String defaultBucketName;
private final MinioClient minioClient;
/**
* 獲取全部bucket
*
* @return all bucket
*/
@SneakyThrows
public List<Bucket> getAllBuckets() {
return minioClient.listBuckets();
}
/**
* 判斷 bucket是否存在
*
* @param bucketName 桶名稱
* @return true 存在
*/
@SneakyThrows
public boolean bucketExists(String bucketName){
BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder()
.bucket(bucketName)
.build();
return minioClient.bucketExists(bucketExistsArgs);
}
/**
* 建立 bucket
*
* @param bucketName 桶名稱
*/
@SneakyThrows
public void createBucket(String bucketName){
boolean isExist = this.bucketExists(bucketName);
if(!isExist) {
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder()
.bucket(bucketName)
.build();
minioClient.makeBucket(makeBucketArgs);
}
}
/**
* 檔案上傳
*
* @param bucketName 桶名稱
* @param fileName 上傳後的檔名稱
* @param fileAbsolutePath 檔案的絕對路徑
* @return 檔案url
*/
@SneakyThrows
public ObjectWriteResponse upload(String bucketName, String fileName, String fileAbsolutePath){
UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
.bucket(bucketName)
.filename(fileAbsolutePath)
.object(fileName)
.build();
return minioClient.uploadObject(uploadObjectArgs);
}
/**
* 檔案上傳
*
* @param fileName 上傳後的檔名稱
* @param stream 檔案輸入流
* @return 檔案url
*/
@SneakyThrows
public String upload(String fileName, InputStream stream){
this.upload(defaultBucketName, fileName, stream);
return getFileUrl(defaultBucketName, fileName);
}
/**
* 檔案上傳
*
* @param bucketName 桶名稱
* @param fileName 上傳後的檔名稱
* @param stream 檔案輸入流
* @return 檔案url
*/
@SneakyThrows
public ObjectWriteResponse upload(String bucketName, String fileName, InputStream stream){
try {
PutObjectArgs objectArgs = PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(stream, stream.available(), -1)
.build();
return minioClient.putObject(objectArgs);
}
catch(Exception e) {
log.error(e.getMessage());
}
finally {
try {
if(Objects.nonNull(stream)) {
stream.close();
}
} catch(IOException e) {
log.error(e.getMessage());
}
}
return null;
}
/**
* 檔案上傳
*
* @param file 檔案
* @return 檔案url
*/
public ObjectWriteResponse upload(MultipartFile file) {
return this.upload(defaultBucketName, file);
}
/**
* 檔案上傳
*
* @param bucketName 桶名稱
* @param file 檔案
* @return 檔案url
*/
public ObjectWriteResponse upload(String bucketName, MultipartFile file) {
InputStream is = null;
try {
is = file.getInputStream();
final String fileName = file.getOriginalFilename();
String contentType = file.getContentType();
PutObjectArgs objectArgs = PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(is, is.available(), -1)
.contentType(contentType)
.build();
return minioClient.putObject(objectArgs);
}
catch(Exception e) {
log.error(e.getMessage());
}
finally {
try {
if(Objects.nonNull(is)) {
is.close();
}
} catch(IOException e) {
log.error(e.getMessage());
}
}
return null;
}
/**
* 附件下載
*
* @param fileName 附件名稱
*/
@SneakyThrows
public void download(String fileName, HttpServletResponse response) {
this.download(defaultBucketName, fileName, response);
}
/**
* 附件下載
*
* @param bucketName 桶名稱
* @param fileName 附件名稱
*/
@SneakyThrows
public void download(String bucketName, String fileName, HttpServletResponse response) {
GetObjectArgs build = GetObjectArgs.builder().bucket(bucketName).object(fileName).build();
OutputStream out = null;
try(GetObjectResponse object = minioClient.getObject(build)) {
int len = 0;
byte[] buffer = new byte[1024];
out = response.getOutputStream();
response.reset();
String fileName1 = new String(fileName.getBytes(), StandardCharsets.ISO_8859_1);
response.addHeader("Content-Disposition", " attachment;filename=" + fileName1);
response.setContentType("application/octet-stream");
while((len = object.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} catch(Exception e) {
log.error(e.getMessage());
} finally {
if(out != null) {
try {
out.close();
} catch(IOException e) {
log.error(e.getMessage());
}
}
}
}
/**
* 附件下載
*
* @param fileName 附件名稱
*/
@SneakyThrows
public void download(String bucketName, String fileName, String fileAbsolutePath) {
DownloadObjectArgs downloadObjectArgs = DownloadObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.filename(fileAbsolutePath)
.build();
minioClient.downloadObject(downloadObjectArgs);
}
/**
* 刪除檔案
*
* @param fileName 檔名稱
*/
@SneakyThrows
public void delFile(String fileName){
this.delFile(defaultBucketName, fileName);
}
/**
* 刪除檔案
*
* @param bucketName 桶名稱
* @param fileName 檔名稱
*/
@SneakyThrows
public void delFile(String bucketName, String fileName){
RemoveObjectArgs removeObjectsArgs = RemoveObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.build();
minioClient.removeObject(removeObjectsArgs);
}
/**
* 獲取minio檔案的下載地址
*
* @param fileName 檔名
*/
@SneakyThrows
public String getFileUrl(String fileName) {
return this.getFileUrl(defaultBucketName, fileName);
}
/**
* 獲取minio檔案的下載地址
*
* @param bucketName 桶名稱
* @param fileName 檔名
*/
@SneakyThrows
public String getFileUrl(String bucketName, String fileName) {
GetPresignedObjectUrlArgs objectUrlArgs = GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(fileName)
.build();
return minioClient.getPresignedObjectUrl(objectUrlArgs);
}
/**
* 獲取minio檔案的下載地址
*
* @param fileName 檔名
*/
@SneakyThrows
public String getFileUrl(String fileName, Integer duration, TimeUnit unit) {
return this.getFileUrl(defaultBucketName, fileName, duration, unit);
}
/**
* 獲取minio檔案的下載地址
*
* @param bucketName 桶名稱
* @param fileName 檔名
*/
@SneakyThrows
public String getFileUrl(String bucketName, String fileName, Integer duration, TimeUnit unit) {
GetPresignedObjectUrlArgs objectUrlArgs = GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(fileName)
.expiry(duration, unit)
.build();
return minioClient.getPresignedObjectUrl(objectUrlArgs);
}
/**
* 設定桶策略
*
* @param bucketName 桶名稱
* @param policy 策略
*/
@SneakyThrows
public void setBucketPolicy(String bucketName, String policy) {
SetBucketPolicyArgs bucketPolicyArgs = SetBucketPolicyArgs.builder()
.bucket(bucketName)
.config(policy)
.build();
minioClient.setBucketPolicy(bucketPolicyArgs);
}
}
4.3 測試類
package com.ldx.minio.controller;
import com.ldx.minio.util.MinioUtils;
import io.minio.ObjectWriteResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
/**
* minio 控制器
*
* @author ludangxin
* @date 2021/8/18
*/
@RestController
@RequestMapping("minio")
@RequiredArgsConstructor
public class MinioController {
private final MinioUtils minioUtils;
@GetMapping
public String getUrl(String fileName) {
return minioUtils.getFileUrl(fileName);
}
@PostMapping
public ObjectWriteResponse upload(MultipartFile file) {
return minioUtils.upload(file);
}
@GetMapping("download/{fileName}")
public void download(@PathVariable String fileName, HttpServletResponse response) {
minioUtils.download(fileName, response);
}
@GetMapping("download/{bucketName}/{fileName}")
public void download(@PathVariable String bucketName, @PathVariable String fileName) {
minioUtils.download(bucketName, fileName, "/Users/ludangxin/temp/" + fileName);
}
@DeleteMapping
public void del(String fileName) {
minioUtils.delFile(fileName);
}
}