SpringBoot整合Minio實現(xiàn)文件上傳和讀取功能
最近公司有一個需求是關于視頻上傳播放的,需要設計一個方案,中間談到了Minio這個技術,于是來學習一下
一、簡介
1.分布式文件系統(tǒng)應用場景
互聯(lián)網(wǎng)海量非結構化數(shù)據(jù)的存儲需求
- 電商網(wǎng)絡:海量商品圖片
- 視頻網(wǎng)站:海量視頻文件
- 網(wǎng)盤:海量文件
- 社交網(wǎng)站:海量圖片
2.Minio介紹
- Go語言開發(fā),開源,免費的對象存儲服務,可以存儲海量非結構化的數(shù)據(jù),一個對象文件可以是任意大小,從幾kb到最大5T不等
3.Minio優(yōu)點
- 部署簡單
- 讀寫性能優(yōu)異
- 支持海量存儲
二、docker部署(windows系統(tǒng))
這里我是用自己電腦(windows系統(tǒng))安裝了docker,然后使用docker來部署的
中文官網(wǎng):單節(jié)點多硬盤部署MinIO — MinIO中文文檔 | MinIO Container中文文檔
1.創(chuàng)建目錄
先進入D盤,創(chuàng)建docker的工作目錄 docker_workplace
,再創(chuàng)建minio的目錄minio
,再創(chuàng)建兩個文件夾data
和config
,如圖所示
拉取鏡像,創(chuàng)建容器并運行
2.拉取鏡像
直接cmd,敲docker命令即可,和linux的語法一樣
3.創(chuàng)建容器并運行
多行:
docker run -d --name minio \ --privileged=true \ --restart=always \ -p 9000:9000 -p 50000:50000 \ -e "MINIO_ROOT_USER=minio" \ -e "MINIO_ROOT_PASSWORD=miniominio" \ -v D:/docker_workplace/data/minio/config:/root/.minio \ -v D:/docker_workplace/data/minio/data1:/data1 \ -v D:/docker_workplace/data/minio/data2:/data2 \ -v D:/docker_workplace/data/minio/data3:/data3 \ -v D:/docker_workplace/data/minio/data4:/data4 \ minio/minio \ server \ --console-address ":50000" /data{1...4}
單行:
docker run -p 9000:9000 -p 50000:50000 -d --name minio -e "MINIO_ACCESS_KEY=minio" -e "MINIO_SECRET_KEY=miniominio" -v D:/docker_workplace/data/minio/config:/root/.minio -v D:/docker_workplace/data/minio/data1:/data1 -v D:/docker_workplace/data/minio/data2:/data2 -v D:/docker_workplace/data/minio/data3:/data3 -v D:/docker_workplace/data/minio/data4:/data4 --restart always minio/minio server --console-address ":50000" /data{1...4}
4.訪問控制臺
瀏覽器訪問:
http://localhost:50000/login
賬號/密碼(剛剛docker運行命令設置的):minio/miniominio
這里我第一次訪問失敗了,看了下docker日志,發(fā)現(xiàn)是賬號密碼長度不符合規(guī)范導致(一開始密碼是
minio
,達不到8個字符)
于是改了密碼為
miniominio
,成功運行
5.初始化配置
創(chuàng)建一個桶 Bucket
這里出現(xiàn)了一個報錯,原因是說需要分布式部署才能使用,解決辦法:掛載多個卷
配置桶權限為public
到這里,minio單機版就部署好了
三、Spring Boot整合Minio
1.創(chuàng)建demo項目
新建一個spring boot項目
2.引入依賴
<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.3.7</version> </dependency>
3.配置
application.yml
spring: application: name: miniodemo servlet: multipart: # 文件上傳大小限制。超過該值直接報錯 max-file-size: 20MB # 文件最大請求限制,用于批量上傳 max-request-size: 20MB datasource: url: jdbc:mysql://localhost:3306/minio?useSSL=false&serverTimezone=UTC username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver # MinIO 配置 minio: endpoint: http://localhost:9000 # MinIO服務地址 fileHost: http://localhost:9000 # 文件地址host bucketName: test # 存儲桶bucket名稱 accessKey: minio # 用戶名 secretKey: miniominio # 密碼 imgSize: 20 # 圖片大小限制,單位:m fileSize: 20 # 文件大小限制,單位:m mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.xuyue.miniodemo.domain
4.編寫配置類
MinIOConfig.java
package com.xuyue.miniodemo.config; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; @Configuration @Data public class MinIOConfig { @Value("${minio.endpoint}") private String endpoint; @Value("${minio.fileHost}") private String fileHost; @Value("${minio.bucketName}") private String bucketName; @Value("${minio.accessKey}") private String accessKey; @Value("${minio.secretKey}") private String secretKey; @Value("${minio.imgSize}") private Integer imgSize; @Value("${minio.fileSize}") private Integer fileSize; }
5.MinIO工具類
MinIoUploadService.java
package com.xuyue.miniodemo.service; import com.xuyue.miniodemo.config.MinIOConfig; import io.minio.*; import io.minio.http.Method; import io.minio.messages.Bucket; import io.minio.messages.DeleteObject; import io.minio.messages.Item; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Optional; /** * MinIO工具類 */ @Slf4j @Service public class MinIoUploadService { @Resource private MinIOConfig minIOConfig; private MinioClient minioClient; private String endpoint; private String bucketName; private String accessKey; private String secretKey; private Integer imgSize; private Integer fileSize; private final String SEPARATOR = "/"; @PostConstruct public void init() { this.endpoint = minIOConfig.getEndpoint(); this.bucketName = minIOConfig.getBucketName(); this.accessKey = minIOConfig.getAccessKey(); this.secretKey = minIOConfig.getSecretKey(); this.imgSize = minIOConfig.getImgSize(); this.fileSize = minIOConfig.getFileSize(); createMinioClient(); } /** * 創(chuàng)建基于Java端的MinioClient */ public void createMinioClient() { try { if (null == minioClient) { log.info("開始創(chuàng)建 MinioClient..."); minioClient = MinioClient .builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); createBucket(bucketName); log.info("創(chuàng)建完畢 MinioClient..."); } } catch (Exception e) { log.error("MinIO服務器異常:{}", e); } } /** * 獲取上傳文件前綴路徑 * * @return */ public String getBasisUrl() { return endpoint + SEPARATOR + bucketName + SEPARATOR; } /****************************** Operate Bucket Start ******************************/ /** * 啟動SpringBoot容器的時候初始化Bucket * 如果沒有Bucket則創(chuàng)建 * * @throws Exception */ private void createBucket(String bucketName) throws Exception { if (!bucketExists(bucketName)) { minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); } } /** * 判斷Bucket是否存在,true:存在,false:不存在 * * @return * @throws Exception */ public boolean bucketExists(String bucketName) throws Exception { return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); } /** * 獲得Bucket的策略 * * @param bucketName * @return * @throws Exception */ public String getBucketPolicy(String bucketName) throws Exception { String bucketPolicy = minioClient .getBucketPolicy( GetBucketPolicyArgs .builder() .bucket(bucketName) .build() ); return bucketPolicy; } /** * 獲得所有Bucket列表 * * @return * @throws Exception */ public List<Bucket> getAllBuckets() throws Exception { return minioClient.listBuckets(); } /** * 根據(jù)bucketName獲取其相關信息 * * @param bucketName * @return * @throws Exception */ public Optional<Bucket> getBucket(String bucketName) throws Exception { return getAllBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst(); } /** * 根據(jù)bucketName刪除Bucket,true:刪除成功; false:刪除失敗,文件或已不存在 * * @param bucketName * @throws Exception */ public void removeBucket(String bucketName) throws Exception { minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); } /****************************** Operate Bucket End ******************************/ /****************************** Operate Files Start ******************************/ /** * 判斷文件是否存在 * * @param bucketName 存儲桶 * @param objectName 文件名 * @return */ public boolean isObjectExist(String bucketName, String objectName) { boolean exist = true; try { minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); } catch (Exception e) { exist = false; } return exist; } /** * 判斷文件夾是否存在 * * @param bucketName 存儲桶 * @param objectName 文件夾名稱 * @return */ public boolean isFolderExist(String bucketName, String objectName) { boolean exist = false; try { Iterable<Result<Item>> results = minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build()); for (Result<Item> result : results) { Item item = result.get(); if (item.isDir() && objectName.equals(item.objectName())) { exist = true; } } } catch (Exception e) { exist = false; } return exist; } /** * 根據(jù)文件前綴查詢文件 * * @param bucketName 存儲桶 * @param prefix 前綴 * @param recursive 是否使用遞歸查詢 * @return MinioItem 列表 * @throws Exception */ public List<Item> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) throws Exception { List<Item> list = new ArrayList<>(); Iterable<Result<Item>> objectsIterator = minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build()); if (objectsIterator != null) { for (Result<Item> o : objectsIterator) { Item item = o.get(); list.add(item); } } return list; } /** * 獲取文件流 * * @param bucketName 存儲桶 * @param objectName 文件名 * @return 二進制流 */ public InputStream getObject(String bucketName, String objectName) throws Exception { return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 斷點下載 * * @param bucketName 存儲桶 * @param objectName 文件名稱 * @param offset 起始字節(jié)的位置 * @param length 要讀取的長度 * @return 二進制流 */ public InputStream getObject(String bucketName, String objectName, long offset, long length) throws Exception { return minioClient.getObject( GetObjectArgs.builder() .bucket(bucketName) .object(objectName) .offset(offset) .length(length) .build()); } /** * 獲取路徑下文件列表 * * @param bucketName 存儲桶 * @param prefix 文件名稱 * @param recursive 是否遞歸查找,false:模擬文件夾結構查找 * @return 二進制流 */ public Iterable<Result<Item>> listObjects(String bucketName, String prefix, boolean recursive) { return minioClient.listObjects( ListObjectsArgs.builder() .bucket(bucketName) .prefix(prefix) .recursive(recursive) .build()); } /** * 使用MultipartFile進行文件上傳 * * @param bucketName 存儲桶 * @param file 文件名 * @param objectName 對象名 * @param contentType 類型 * @return * @throws Exception */ public ObjectWriteResponse uploadFile(String bucketName, MultipartFile file, String objectName, String contentType) throws Exception { InputStream inputStream = file.getInputStream(); return minioClient.putObject( PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .contentType(contentType) .stream(inputStream, inputStream.available(), -1) .build()); } /** * 上傳本地文件 * * @param bucketName 存儲桶 * @param objectName 對象名稱 * @param fileName 本地文件路徑 */ public ObjectWriteResponse uploadFile(String bucketName, String objectName, String fileName) throws Exception { return minioClient.uploadObject( UploadObjectArgs.builder() .bucket(bucketName) .object(objectName) .filename(fileName) .build()); } /** * 通過流上傳文件 * * @param bucketName 存儲桶 * @param objectName 文件對象 * @param inputStream 文件流 */ public ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) throws Exception { return minioClient.putObject( PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .stream(inputStream, inputStream.available(), -1) .build()); } /** * 創(chuàng)建文件夾或目錄 * * @param bucketName 存儲桶 * @param objectName 目錄路徑 */ public ObjectWriteResponse createDir(String bucketName, String objectName) throws Exception { return minioClient.putObject( PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .stream(new ByteArrayInputStream(new byte[]{}), 0, -1) .build()); } /** * 獲取文件信息, 如果拋出異常則說明文件不存在 * * @param bucketName 存儲桶 * @param objectName 文件名稱 */ public String getFileStatusInfo(String bucketName, String objectName) throws Exception { return minioClient.statObject( StatObjectArgs.builder() .bucket(bucketName) .object(objectName) .build()).toString(); } /** * 拷貝文件 * * @param bucketName 存儲桶 * @param objectName 文件名 * @param srcBucketName 目標存儲桶 * @param srcObjectName 目標文件名 */ public ObjectWriteResponse copyFile(String bucketName, String objectName, String srcBucketName, String srcObjectName) throws Exception { return minioClient.copyObject( CopyObjectArgs.builder() .source(CopySource.builder().bucket(bucketName).object(objectName).build()) .bucket(srcBucketName) .object(srcObjectName) .build()); } /** * 刪除文件 * * @param bucketName 存儲桶 * @param objectName 文件名稱 */ public void removeFile(String bucketName, String objectName) throws Exception { minioClient.removeObject( RemoveObjectArgs.builder() .bucket(bucketName) .object(objectName) .build()); } /** * 批量刪除文件 * * @param bucketName 存儲桶 * @param keys 需要刪除的文件列表 * @return */ public void removeFiles(String bucketName, List<String> keys) { List<DeleteObject> objects = new LinkedList<>(); keys.forEach(s -> { objects.add(new DeleteObject(s)); try { removeFile(bucketName, s); } catch (Exception e) { log.error("批量刪除失?。rror:{}", e); } }); } /** * 獲取文件外鏈 * * @param bucketName 存儲桶 * @param objectName 文件名 * @param expires 過期時間 <=7 秒 (外鏈有效時間(單位:秒)) * @return url * @throws Exception */ public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) throws Exception { GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().expiry(expires).bucket(bucketName).object(objectName).build(); return minioClient.getPresignedObjectUrl(args); } /** * 獲得文件外鏈 * * @param bucketName * @param objectName * @return url * @throws Exception */ public String getPresignedObjectUrl(String bucketName, String objectName) throws Exception { GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder() .bucket(bucketName) .object(objectName) .method(Method.GET).build(); return minioClient.getPresignedObjectUrl(args); } /** * 將URLDecoder編碼轉成UTF8 * * @param str * @return * @throws UnsupportedEncodingException */ public String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException { String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25"); return URLDecoder.decode(url, "UTF-8"); } /****************************** Operate Files End ******************************/ }
6.文件上傳
FileController.java
package com.xuyue.miniodemo.controller; import com.xuyue.miniodemo.config.MinIOConfig; import com.xuyue.miniodemo.service.MinIoUploadService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @Slf4j public class FileController { @Autowired private MinIoUploadService minIoUploadService; @Autowired private MinIOConfig minIOConfig; /** * 上傳文件,返回url * @param file * @return * @throws Exception */ @PostMapping("upload") public String upload(MultipartFile file) throws Exception { String fileName = file.getOriginalFilename(); minIoUploadService.uploadFile(minIOConfig.getBucketName(), fileName, file.getInputStream()); String imgUrl = minIOConfig.getFileHost() + "/" + minIOConfig.getBucketName() + "/" + fileName; return imgUrl; } }
測試
啟動項目
使用postman請求接口,返回文件地址
訪問地址:
查看minio控制臺:
以上就是SpringBoot整合Minio實現(xiàn)文件上傳和讀取功能的詳細內容,更多關于SpringBoot Minio文件上傳讀取的資料請關注腳本之家其它相關文章!
相關文章
解決mybatis plus報錯com.microsoft.sqlserver.jdbc.SQLServerE
這篇文章主要介紹了解決mybatis plus報錯com.microsoft.sqlserver.jdbc.SQLServerException:必須執(zhí)行該語句才能獲得結果,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05