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

3.1.2 啟動(dòng)容器
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 訪(fǎng)問(wèn)web頁(yè)面
容器啟動(dòng)成功后,注意開(kāi)發(fā)相關(guān)的防火墻端口即可,然后訪(fǎng)問(wèn)地址:IP:9000,即可訪(fǎng)問(wèn)Minio的web界面

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

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



給當(dāng)前這個(gè)bucket上傳一個(gè)文件

點(diǎn)擊文件夾圖標(biāo)

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

4.3 用戶(hù)管理
針對(duì)客戶(hù)端的操作,經(jīng)常需要維護(hù)相關(guān)的賬號(hào)來(lái)管理,比如賬戶(hù)的操作權(quán)限,訪(fǎng)問(wèn)控制等;
點(diǎn)擊,創(chuàng)建用戶(hù)

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

然后在用戶(hù)列表就可以看到這個(gè)用戶(hù)了

4.4 Java操作Minio
通過(guò)上面的環(huán)境搭建和操作,演示并了解了如何快速使用Minio,更多的功能可以參閱相關(guān)資料進(jìn)一步學(xué)習(xí)了解,下面我們編寫(xiě)java代碼完成文件的上傳。
4.4.1 導(dǎo)入依賴(lài)
在當(dāng)前的maven工程中導(dǎo)入minio的依賴(lài),客戶(hù)端具體版本可以根據(jù)你的實(shí)際需要選擇
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>4.4.2 上傳文件到minio
通過(guò)下面這段代碼將本地的一張圖片文件上傳到minio的test這個(gè)bucket目錄下
public static void main(String[] args) {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("F:\\網(wǎng)盤(pán)\\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();
}
}
}運(yùn)行這段代碼,上傳成功后,去到test的目錄下刷新之后可以看到文件已經(jīng)上傳


五、springboot整合Minio
接下來(lái)讓我們看看如何在springboot中集成Minio,參考下面的操作步驟
5.1 前置準(zhǔn)備
5.1.1 引入依賴(lài)
創(chuàng)建一個(gè)maven工程,引入如下相關(guān)的依賴(lài)
<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 編碼過(guò)程
5.2.1 創(chuàng)建一個(gè)參數(shù)配置類(lèi)
通過(guò)這種方式將配置文件中以minio開(kāi)頭的那些配置加載到spring容器中管理,其他位置使用的時(shí)候直接注入即可
@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配置類(lèi)
通過(guò)這個(gè)全局的配置類(lèi),其他需要上傳文件的類(lèi)中直接注入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文件服務(wù)類(lèi)或工具類(lèi)
在日常開(kāi)發(fā)中,可以自定義一個(gè)minio的工具類(lèi),使用的時(shí)候就比較方便了,下面的代碼中列舉了常用的一些API操作方法,可以結(jié)合實(shí)際需要自定義更多的工具方法
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();
// 檢查存儲(chǔ)桶是否已經(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);
//組裝桶中文件的訪(fǎng)問(wèn)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();
// 有對(duì)象文件,則刪除失敗
if (item.size() > 0) {
return false;
}
}
// 刪除存儲(chǔ)桶,注意,只有存儲(chǔ)桶為空時(shí)才能刪除成功。
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 編寫(xiě)測(cè)試接口
為了方便測(cè)試,下面定義了一個(gè)測(cè)試接口
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名稱(chēng)
* @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的所有對(duì)象名稱(chēng)
* @param bucketName
* @return
* @throws Exception
*/
@PostMapping("/list/object_names")
public List<String> listObjectNames(@RequestParam("bucketName")String bucketName) throws Exception {
return minioFileService.listObjectNames(bucketName);
}
/**
* 刪除bucket中的某個(gè)對(duì)象
* @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);
}
/**
* 獲取文件訪(fǎng)問(wèn)路徑
* @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 接口測(cè)試
上傳文件測(cè)試
上傳成功后,返回了文件的完整路徑,方便后續(xù)使用

然后在控制臺(tái)的test這個(gè)bucket中檢查是否上傳上去了

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

更多的接口有興趣的同學(xué)可以一一嘗試下,就不再贅述了。
六、寫(xiě)在文末
本文詳細(xì)總結(jié)了Minio的搭建使用,以及與springboot整合的完整步驟,作為一款適用且輕量級(jí)的文件存儲(chǔ)服務(wù)器,可以私有化部署,也可以很方便進(jìn)行云上部署、容器化部署,為今后在實(shí)際項(xiàng)目中的技術(shù)選型提供一個(gè)參考,本篇到此結(jié)束,感謝觀看。
以上就是springboot整合minio的超詳細(xì)教程的詳細(xì)內(nèi)容,更多關(guān)于springboot整合minio的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot整合MinIO實(shí)現(xiàn)文件上傳的方法詳解
- springboot整合minio實(shí)現(xiàn)文件上傳與下載且支持鏈接永久訪(fǎng)問(wèn)
- 可能是全網(wǎng)最詳細(xì)的springboot整合minio教程
- SpringBoot整合Minio實(shí)現(xiàn)文件上傳和讀取功能
- SpringBoot整合Minio實(shí)現(xiàn)圖片上傳功能
- 手把手教你SpringBoot輕松整合Minio
- SpringBoot使用minio及配置代碼
- SpringBoot項(xiàng)目集成MinIO全過(guò)程
- SpringBoot封裝MinIO工具的實(shí)現(xiàn)步驟
相關(guān)文章
java使用httpclient 發(fā)送請(qǐng)求的示例
HttpClient 是Apache Jakarta Common 下的子項(xiàng)目,可以用來(lái)提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶(hù)端編程工具包,并且它支持 HTTP 協(xié)議最新的版本和建議,這篇文章主要介紹了java使用httpclient 發(fā)送請(qǐng)求的示例,需要的朋友可以參考下2023-10-10
自動(dòng)配置@EnableAutoConfiguration問(wèn)題
這篇文章主要介紹了自動(dòng)配置@EnableAutoConfiguration問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
java數(shù)組的三種擴(kuò)容方式以及程序?qū)崿F(xiàn)詳解
這篇文章主要介紹了java數(shù)組的三種擴(kuò)容方式以及程序?qū)崿F(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
idea項(xiàng)目debug模式啟動(dòng),斷點(diǎn)失效,斷點(diǎn)紅點(diǎn)內(nèi)無(wú)對(duì)勾問(wèn)題及解決
這篇文章主要介紹了idea項(xiàng)目debug模式啟動(dòng),斷點(diǎn)失效,斷點(diǎn)紅點(diǎn)內(nèi)無(wú)對(duì)勾問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
Java多線(xiàn)程:生產(chǎn)者與消費(fèi)者案例
這篇文章主要介紹了Java并發(fā)編程中的生產(chǎn)者與消費(fèi)者模型簡(jiǎn)述,多線(xiàn)程并發(fā)是Java編程中最終要的部分之一,需要的朋友可以參考下,希望能給你帶來(lái)幫助2021-07-07

