springboot整合minio的超詳細教程
一、前言
在很多互聯(lián)網(wǎng)產(chǎn)品應用中,都涉及到各種與文件存儲相關的業(yè)務,隨著技術(shù)的發(fā)展,關于如何解決分布式文件存儲也有了比較成熟的方案,比如私有云部署下可以考慮fastdfs,阿里云對象存儲oss,七牛云等,本篇將為你介紹另一種文件存儲方式,即MinIO 。
二、Minio 概述
2.1 Minio簡介
MinIO基于Apache License v2.0開源協(xié)議的對象存儲服務,可以做為云存儲的解決方案用來保存海量的圖片,視頻,文檔,是一款高性能、分布式的對象存儲系統(tǒng), 可以100%的運行在標準硬件,即X86等低成本機器也能夠很好的運行MinIO。
傳統(tǒng)的存儲和其他的對象存儲不同的是:
它一開始就針對性能要求更高的私有云標準進行軟件架構(gòu)設計。因為MinIO一開始就只為對象存儲而設計。所以他采用了更易用的方式進行設計,它能實現(xiàn)對象存儲所需要的全部功能,在性能上也更加強勁,它不會為了更多的業(yè)務功能而妥協(xié),失去MinIO的易用性、高效性。 這樣的結(jié)果所帶來的好處是:它能夠更簡單的實現(xiàn)局有彈性伸縮能力的原生對象存儲服務。
2.2 Minio特點
Minio具有如下特點
- 性能高,準硬件條件下它能達到55GB/s的讀、35GB/s的寫速率;
- 部署自帶管理界面;
- MinIO.Inc運營的開源項目,社區(qū)活躍度高;
- 提供了所有主流開發(fā)語言的SDK;
- 基于Golang語言實現(xiàn),配置簡單,單行命令可以運行起來;
- 兼容亞馬遜S3云存儲服務接口,適合于存儲大容量非結(jié)構(gòu)化的數(shù)據(jù),一個對象文件可以是任意大小,從幾kb到最大5T不等;
三、Minio 環(huán)境搭建
本文采用docker的方式快速搭建起Minio的環(huán)境,也可以通過官網(wǎng)下載安裝包部署,官網(wǎng)安裝包下載地址
3.1 部署過程
3.1.1 拉取鏡像
docker pull minio/minio

3.1.2 啟動容器
docker run -d -p 9000:9000 -p 9090:9090 \
--name minio \
-e "MINIO_ACCESS_KEY=minio" \
-e "MINIO_SECRET_KEY=minio" \
-v /home/minio/data:/data \
-v /home/minio/config:/root/.minio \
minio/minio server \
/data --console-address ":9090" -address ":9000"
3.1.3 訪問web頁面
容器啟動成功后,注意開發(fā)相關的防火墻端口即可,然后訪問地址:IP:9000,即可訪問Minio的web界面

輸入賬戶和密碼,登錄進去之后,看到下面的效果說明Minio環(huán)境搭建完成

四、Minio基本使用
4.1 基本概念
在正式開始使用Minio之前,有必要先了解下幾個相關的概念
- bucket ,類比于文件系統(tǒng)的目錄;
- Object ,類比文件系統(tǒng)的文件;
- Keys ,類比文件名;
4.2 上傳文件演示
如下,點擊創(chuàng)建一個新的bucket,創(chuàng)建完成后就可以在列表上看到這個bucket;



給當前這個bucket上傳一個文件

點擊文件夾圖標

上傳一張本地文件,上傳完成后就可以看到這個文件了,也可以瀏覽上傳的文件

4.3 用戶管理
針對客戶端的操作,經(jīng)常需要維護相關的賬號來管理,比如賬戶的操作權(quán)限,訪問控制等;
點擊,創(chuàng)建用戶

填寫用戶信息,勾選權(quán)限保存即可

然后在用戶列表就可以看到這個用戶了

4.4 Java操作Minio
通過上面的環(huán)境搭建和操作,演示并了解了如何快速使用Minio,更多的功能可以參閱相關資料進一步學習了解,下面我們編寫java代碼完成文件的上傳。
4.4.1 導入依賴
在當前的maven工程中導入minio的依賴,客戶端具體版本可以根據(jù)你的實際需要選擇
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>4.4.2 上傳文件到minio
通過下面這段代碼將本地的一張圖片文件上傳到minio的test這個bucket目錄下
public static void main(String[] args) {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("F:\\網(wǎng)盤\\Title-logo.png");
MinioClient client = MinioClient.builder().credentials("minio", "minio").endpoint("http://IP:9000").build();
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object("logo.png")
.contentType("image/png")
.bucket("test")
.stream(inputStream, inputStream.available(), -1)
.build();
client.putObject(putObjectArgs);
System.out.println("上傳成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}運行這段代碼,上傳成功后,去到test的目錄下刷新之后可以看到文件已經(jīng)上傳


五、springboot整合Minio
接下來讓我們看看如何在springboot中集成Minio,參考下面的操作步驟
5.1 前置準備
5.1.1 引入依賴
創(chuàng)建一個maven工程,引入如下相關的依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>5.1.2 核心配置文件
在配置文件中添加如下內(nèi)容
server:
port: 8087
logging:
level:
root: info
com.congge.model: debug
minio:
username: minio
pwd: minio
bucket: test
endpoint: http://IP:9000
readPath: http://IP:90005.2 編碼過程
5.2.1 創(chuàng)建一個參數(shù)配置類
通過這種方式將配置文件中以minio開頭的那些配置加載到spring容器中管理,其他位置使用的時候直接注入即可
@Data
@ConfigurationProperties(prefix = "minio")
@Component
public class MinIOConfigProperties implements Serializable {
private String username;
private String pwd;
private String bucket;
private String endpoint;
private String readPath;
}5.2.2 創(chuàng)建minio配置類
通過這個全局的配置類,其他需要上傳文件的類中直接注入MinioClient即可
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
public class MinIOConfig {
@Autowired
private MinIOConfigProperties minIOConfigProperties;
@Bean
public MinioClient buildMinioClient() {
return MinioClient
.builder()
.credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
.endpoint(minIOConfigProperties.getEndpoint())
.build();
}
}5.2.3 創(chuàng)建minio文件服務類或工具類
在日常開發(fā)中,可以自定義一個minio的工具類,使用的時候就比較方便了,下面的代碼中列舉了常用的一些API操作方法,可以結(jié)合實際需要自定義更多的工具方法
import com.congge.config.MinIOConfig;
import com.congge.config.MinIOConfigProperties;
import com.congge.service.MinioFileService;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Slf4j
@Import(MinIOConfig.class)
@Service
public class MinioFileServiceImpl implements MinioFileService {
@Autowired
private MinioClient minioClient;
@Autowired
private MinIOConfigProperties minIOConfigProperties;
private final static String separator = "/";
/**
* 下載文件
*
* @param pathUrl 文件全路徑
* @return 文件流
*/
@Override
public void downLoadFile(String pathUrl, HttpServletResponse response) {
String[] pathItems = pathUrl.split("/");
String originFileName = pathItems[pathItems.length - 1];
String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
int index = key.indexOf(separator);
//String bucket = key.substring(0,index);
String filePath = key.substring(index + 1);
InputStream inputStream = null;
try {
inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build());
response.setHeader("Content-Disposition", "attachment;filename=" + originFileName);
response.setContentType("application/force-download");
response.setCharacterEncoding("UTF-8");
IOUtils.copy(inputStream, response.getOutputStream());
System.out.println("下載成功");
} catch (Exception e) {
log.error("minio down file error. pathUrl:{}", pathUrl);
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public String uploadFile(MultipartFile file) throws Exception {
String bucketName = minIOConfigProperties.getBucket();
String endpoint = minIOConfigProperties.getEndpoint();
// 檢查存儲桶是否已經(jīng)存在
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (isExist) {
System.out.println("Bucket already exists.");
} else {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
String originalFilename = file.getOriginalFilename();
//拼接生成新的UUID形式的文件名
String objectName = new SimpleDateFormat("yyyy/MM/dd/").format(new Date()) +
UUID.randomUUID().toString().replaceAll("-", "")
+ originalFilename.substring(originalFilename.lastIndexOf("."));
PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName)
.bucket(bucketName)
.contentType(file.getContentType())
.stream(file.getInputStream(), file.getSize(), -1).build();
minioClient.putObject(objectArgs);
//組裝桶中文件的訪問url
String resUrl = endpoint + "/" + bucketName + "/" + objectName;
return resUrl;
}
/**
* 刪除文件
*
* @param pathUrl 文件全路徑
*/
@Override
public void delete(String pathUrl) {
String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
int index = key.indexOf(separator);
String bucket = key.substring(0, index);
String filePath = key.substring(index + 1);
// 刪除Objects
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
try {
minioClient.removeObject(removeObjectArgs);
} catch (Exception e) {
log.error("minio remove file error. pathUrl:{}", pathUrl);
e.printStackTrace();
}
}
public List<Bucket> listBuckets()
throws Exception {
return minioClient.listBuckets();
}
public boolean bucketExists(String bucketName) throws Exception {
boolean flag = minioClient.bucketExists(bucketName);
if (flag) {
return true;
}
return false;
}
@Override
public List<String> listBucketNames() throws Exception{
List<Bucket> bucketList = listBuckets();
List<String> bucketListName = new ArrayList<>();
for (Bucket bucket : bucketList) {
bucketListName.add(bucket.name());
}
return bucketListName;
}
@Override
public boolean makeBucket(String bucketName) throws Exception{
boolean flag = bucketExists(bucketName);
if (!flag) {
minioClient.makeBucket(bucketName);
return true;
} else {
return false;
}
}
@Override
public boolean removeBucket(String bucketName) throws Exception{
boolean flag = bucketExists(bucketName);
if (flag) {
Iterable<Result<Item>> myObjects = listObjects(bucketName);
for (Result<Item> result : myObjects) {
Item item = result.get();
// 有對象文件,則刪除失敗
if (item.size() > 0) {
return false;
}
}
// 刪除存儲桶,注意,只有存儲桶為空時才能刪除成功。
minioClient.removeBucket(bucketName);
flag = bucketExists(bucketName);
if (!flag) {
return true;
}
}
return false;
}
@Override
public List<String> listObjectNames(String bucketName) throws Exception{
List<String> listObjectNames = new ArrayList<>();
boolean flag = bucketExists(bucketName);
if (flag) {
Iterable<Result<Item>> myObjects = listObjects(bucketName);
for (Result<Item> result : myObjects) {
Item item = result.get();
listObjectNames.add(item.objectName());
}
}
return listObjectNames;
}
@Override
public boolean removeObject(String bucketName, String objectName) throws Exception{
boolean flag = bucketExists(bucketName);
if (flag) {
List<String> objectList = listObjectNames(bucketName);
for (String s : objectList) {
if(s.equals(objectName)){
minioClient.removeObject(bucketName, objectName);
return true;
}
}
}
return false;
}
@Override
public String getObjectUrl(String bucketName, String objectName) throws Exception{
boolean flag = bucketExists(bucketName);
String url = "";
if (flag) {
url = minioClient.getObjectUrl(bucketName, objectName);
}
return url;
}
public Iterable<Result<Item>> listObjects(String bucketName) throws Exception {
boolean flag = bucketExists(bucketName);
if (flag) {
return minioClient.listObjects(bucketName);
}
return null;
}
}5.2.4 編寫測試接口
為了方便測試,下面定義了一個測試接口
import com.congge.service.MinioFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@RestController
@Slf4j
public class FileController {
@Autowired
private MinioFileService minioFileService;
/**
* 上傳文件
* @param file
* @return
* @throws Exception
*/
@PostMapping("/upload")
public String upload(@RequestBody MultipartFile file) throws Exception {
String url = minioFileService.uploadFile(file);
return "文件上傳成功,文件路徑:" + url;
}
/**
* 下載文件
* @param pathUrl
* @param response
*/
@GetMapping("/download")
public void download(@RequestParam("pathUrl") String pathUrl, HttpServletResponse response) {
minioFileService.downLoadFile(pathUrl,response);
}
/**
* 列出所有bucket名稱
* @return
* @throws Exception
*/
@PostMapping("/list/bucket")
public List<String> list() throws Exception {
return minioFileService.listBucketNames();
}
/**
* 創(chuàng)建bucket
* @param bucketName
* @return
* @throws Exception
*/
@PostMapping("/create/bucket")
public boolean createBucket(@RequestParam("bucketName")String bucketName) throws Exception {
return minioFileService.makeBucket(bucketName);
}
/**
* 刪除bucket
* @param bucketName
* @return
* @throws Exception
*/
@PostMapping("/delete/bucket")
public boolean deleteBucket(@RequestParam("bucketName")String bucketName) throws Exception {
return minioFileService.removeBucket(bucketName);
}
/**
* 列出bucket的所有對象名稱
* @param bucketName
* @return
* @throws Exception
*/
@PostMapping("/list/object_names")
public List<String> listObjectNames(@RequestParam("bucketName")String bucketName) throws Exception {
return minioFileService.listObjectNames(bucketName);
}
/**
* 刪除bucket中的某個對象
* @param bucketName
* @param objectName
* @return
* @throws Exception
*/
@PostMapping("/remove/object")
public boolean removeObject(@RequestParam("bucketName")String bucketName,@RequestParam("objectName") String objectName) throws Exception {
return minioFileService.removeObject(bucketName, objectName);
}
/**
* 獲取文件訪問路徑
* @param bucketName
* @param objectName
* @return
* @throws Exception
*/
@PostMapping("/get/object/url")
public String getObjectUrl(@RequestParam("bucketName")String bucketName, @RequestParam("objectName")String objectName) throws Exception {
return minioFileService.getObjectUrl(bucketName, objectName);
}
}5.2.5 接口測試
上傳文件測試
上傳成功后,返回了文件的完整路徑,方便后續(xù)使用

然后在控制臺的test這個bucket中檢查是否上傳上去了

下載文件測試
使用上一步返回的url,直接調(diào)用下載接口,下載完成后可以直接預覽

更多的接口有興趣的同學可以一一嘗試下,就不再贅述了。
六、寫在文末
本文詳細總結(jié)了Minio的搭建使用,以及與springboot整合的完整步驟,作為一款適用且輕量級的文件存儲服務器,可以私有化部署,也可以很方便進行云上部署、容器化部署,為今后在實際項目中的技術(shù)選型提供一個參考,本篇到此結(jié)束,感謝觀看。
以上就是springboot整合minio的超詳細教程的詳細內(nèi)容,更多關于springboot整合minio的資料請關注腳本之家其它相關文章!
相關文章
淺談SpringSecurity注解與AOP切面執(zhí)行順序
這篇文章主要介紹了淺談SpringSecurity注解與AOP切面執(zhí)行順序,引入Spring Security后,在Controller的方法中會出現(xiàn)Spring Security的方法注解與AOP同時存在的問題,這是就會設計順序問題,需要的朋友可以參考下2023-10-10
一文搞懂JMeter engine中HashTree的配置問題
本文主要介紹了JMeter engine中HashTree的配置,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
SpringBoot整合EasyExcel實現(xiàn)導入導出功能
EasyExcel是一個基于Java的、快速、簡潔、解決大文件內(nèi)存溢出的Excel處理工具,他能讓你在不用考慮性能、內(nèi)存的等因素的情況下,快速完成Excel的讀、寫等功能,本文就給大家介紹一下SpringBoot整合EasyExcel實現(xiàn)導入導出功能的方法,需要的朋友可以參考下2023-09-09
Java基于Spire Cloud Excel把Excel轉(zhuǎn)換成PDF
這篇文章主要介紹了Java基于Spire Cloud Excel把Excel轉(zhuǎn)換成PDF,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-05-05
SpringBoot實現(xiàn)阿里云快遞物流查詢的示例代碼
本文將基于springboot實現(xiàn)快遞物流查詢,物流信息的獲取通過阿里云第三方實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2021-10-10
List轉(zhuǎn)變?yōu)槎禾柗指舻腟tring(Java7和Java8分別實現(xiàn))
這篇文章主要介紹了Java7和Java8分別實現(xiàn)List轉(zhuǎn)變?yōu)槎禾柗指舻腟tring,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06

