一、Linux 安裝Minio
安裝
在/root/xxkfz/soft
目錄下面創建文件minio
文件夾,進入minio
文件夾,并創建data
目錄;
[root@xxkfz?soft]#?mkdir?minio
[root@xxkfz?soft]#?cd?minio
[root@xxkfz?minio]#?mkdir?data
執行如下命令進行下載
[root@xxkfz?minio]#?wget?https://dl.min.io/server/minio/release/linux-amd64/minio
[root@xxkfz?minio]#?chmod?+x?minio??#?賦權
下載完成后如下所示:
設置賬號密碼
minio 默認賬號密碼為?minioadmin/minioadmin
[root@xxkfz?minio]#?export?MINIO_ACCESS_KEY=admin?#?設置控制臺賬號(最少3位)
[root@xxkfz?minio]#?export?MINIO_SECRET_KEY=12345678?#?設置密碼(最少8位)
直接設置管理員賬號密碼?編輯?/etc/profile
?文件即可
[root@xxkfz?minio]#?vim?/etc/profile
編輯/etc/profile
文件,追加如下內容:
#===============================Minio=============================================
#?set?minio?environment
export?MINIO_ROOT_USER=admin
export?MINIO_ROOT_PASSWORD=admin123
可省略?設置賬號密碼
?此步驟!12
啟動
進入執行文件目錄/root/xxkfz/soft/minio
,自定義端口啟動(默認端口:9000)
[root@xxkfz?minio]#?nohup?/root/xxkfz/soft/minio/minio?server?--address?:9001?--console-address?:9002?/root/xxkfz/soft/minio/data?>/root/xxkfz/soft/minio/minio.log?2>&1?&
說明:
?nohup
?為后臺啟動?./minio server
?啟動命令?--address :9001
?指定API端口?--console-address :9002
?指定控制臺端口?/usr/local/minio/data
?指定存儲目錄?>/usr/local/minio/minio.log 2>&1
?控制臺日志重定向到/usr/local/minio/minio.log文件中?&
?后臺運行
啟動成功:
啟動成功
啟動成功
注意:瀏覽器訪問需要開啟防火墻端口!
阿里云配置開放9001、9002端口
測試訪問:http://IP地址:9002
輸入賬號密碼:?admin/12345678
?登錄成功!
設置開機自啟動
設置Minio服務器宕機后自動重啟
進入init.d目錄
[root@xxkfz?minio]#?cd?/etc/rc.d/init.d
新建minio.sh shell腳本文件
[root@xxkfz?init.d]#?vim?minio.sh?
shell腳本內容
#!/bin/bash
#chkconfig:?2345?10?90
#description:?ping10
nohup?/root/xxkfz/soft/minio/minio?server?--address?:9001?--console-address?:9002?/root/xxkfz/soft/minio/data?>/root/xxkfz/soft/minio/minio.log?2>&1?&
給shell腳本賦權
chmod?+x?minio.sh
添加到開機自啟動服務中
chkconfig?--add?minio.sh
設置開機自啟動
chkconfig?minio.sh?on
查看是否添加成功
chkconfig?--list
二、Spring Boot整合Minio
項目搭建
項目基本結構
項目基本結構
引入依賴
pom.xml
???<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.11</version></dependency>
配置MinIo
application.yml
minio:endpoint:?http://IP地址:9001accessKey:?adminsecretKey:?12345678bucketName:?xk-admin#?配置端口號
server:port:?8099
編寫配置類
MinioConfig.java
/***?@program:?xxkfz-minio*?@ClassName?MinioConfig.java*?@author:?公眾號:小小開發者*?@create:?2024-03-13?10:53*?@description:?Minio?配置類**/
@Data
@Configuration
public?class?MinioConfig?{/***?訪問地址*/@Value("${minio.endpoint}")private?String?endpoint;/***?accessKey類似于用戶ID,用于唯一標識你的賬戶*/@Value("${minio.accessKey}")private?String?accessKey;/***?secretKey是你賬戶的密碼*/@Value("${minio.secretKey}")private?String?secretKey;/***?默認存儲桶*/@Value("${minio.bucketName}")private?String?bucketName;@Beanpublic?MinioClient?minioClient()?{MinioClient?minioClient?=?MinioClient.builder().endpoint(endpoint).credentials(accessKey,?secretKey).build();return?minioClient;}
}
編寫Minio操作工具類
MinioUtils.java
/***?@program:?xxkfz-minio*?@ClassName?MinioUtils.java*?@author:?公眾號:小小開發者*?@create:?2024-03-13?10:55*?@description:?MinIO操作工具類**/
@Slf4j
@Component
public?class?MinioUtils?{@Autowiredprivate?MinioClient?minioClient;/***?啟動SpringBoot容器的時候初始化Bucket*?如果沒有Bucket則創建**?@param?bucketName*/public?void?createBucket(String?bucketName)?{try?{if?(!bucketExists(bucketName))?{minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());log.info("創建bucketName?=?{}完成!",?bucketName);return;}log.info("bucketName?=?{}已存在!策略為:{}",?bucketName,?getBucketPolicy(bucketName));}?catch?(Exception?e)?{log.error("創建bucketName?=?{}異常!e?=?{}",?bucketName,?e);}}/***?判斷Bucket是否存在,true:存在,false:不存在**?@param?bucketName*?@return*/@SneakyThrowspublic?boolean?bucketExists(String?bucketName)?{return?minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}/***?獲得Bucket的策略**?@param?bucketName*?@return*/@SneakyThrowspublic?String?getBucketPolicy(String?bucketName)?{return?minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());}/***?獲得所有Bucket列表**?@return*/@SneakyThrowspublic?List<Bucket>?getAllBuckets()?{return?minioClient.listBuckets();}/***?根據bucketName獲取其相關信息**?@param?bucketName*?@return*/@SneakyThrows(Exception.class)public?Optional<Bucket>?getBucket(String?bucketName)?{return?getAllBuckets().stream().filter(b?->?b.name().equals(bucketName)).findFirst();}/***?根據bucketName刪除Bucket,true:刪除成功;?false:刪除失敗,文件或已不存在**?@param?bucketName*?@throws?Exception*/@SneakyThrows(Exception.class)public?void?removeBucket(String?bucketName)?{minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}/***?判斷文件是否存在**?@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)?{log.error("[Minio工具類]>>>>?判斷文件是否存在,?異常:",?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)?{log.error("[Minio工具類]>>>>?判斷文件夾是否存在,異常:",?e);exist?=?false;}return?exist;}/***?根據文件前置查詢文件**?@param?bucketName?存儲桶*?@param?prefix?????前綴*?@param?recursive??是否使用遞歸查詢*?@return?MinioItem?列表*/@SneakyThrows(Exception.class)public?List<Item>?getAllObjectsByPrefix(String?bucketName,?String?prefix,?boolean?recursive)?{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?二進制流*/@SneakyThrows(Exception.class)public?InputStream?getObject(String?bucketName,?String?objectName)?{return?minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());}/***?斷點下載**?@param?bucketName?存儲桶*?@param?objectName?文件名稱*?@param?offset?????起始字節的位置*?@param?length?????要讀取的長度*?@return?二進制流*/@SneakyThrows(Exception.class)public?InputStream?getObject(String?bucketName,?String?objectName,?long?offset,?long?length)?{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*/@SneakyThrows(Exception.class)public?ObjectWriteResponse?uploadFile(String?bucketName,?MultipartFile?file,?String?objectName,?String?contentType)?{InputStream?inputStream?=?file.getInputStream();return?minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).contentType(contentType).stream(inputStream,?inputStream.available(),?-1).build());}/***?圖片上傳**?@param?bucketName*?@param?imageBase64*?@param?imageName*?@return*/public?ObjectWriteResponse?uploadImage(String?bucketName,?String?imageBase64,?String?imageName)?{if?(!StringUtils.isEmpty(imageBase64))?{InputStream?in?=?base64ToInputStream(imageBase64);String?newName?=?System.currentTimeMillis()?+?"_"?+?imageName?+?".jpg";String?year?=?String.valueOf(new?Date().getYear());String?month?=?String.valueOf(new?Date().getMonth());return?uploadFile(bucketName,?year?+?"/"?+?month?+?"/"?+?newName,?in);}return?null;}public?static?InputStream?base64ToInputStream(String?base64)?{ByteArrayInputStream?stream?=?null;try?{byte[]?bytes?=?Base64.getEncoder().encode(base64.trim().getBytes());stream?=?new?ByteArrayInputStream(bytes);}?catch?(Exception?e)?{e.printStackTrace();}return?stream;}/***?上傳本地文件**?@param?bucketName?存儲桶*?@param?objectName?對象名稱*?@param?fileName???本地文件路徑*?@return*/@SneakyThrows(Exception.class)public?ObjectWriteResponse?uploadFile(String?bucketName,?String?objectName,?String?fileName)?{return?minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());}/***?通過流上傳文件**?@param?bucketName??存儲桶*?@param?objectName??文件對象*?@param?inputStream?文件流*?@return*/@SneakyThrows(Exception.class)public?ObjectWriteResponse?uploadFile(String?bucketName,?String?objectName,?InputStream?inputStream)?{return?minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(inputStream,?inputStream.available(),?-1).build());}/***?創建文件夾或目錄**?@param?bucketName?存儲桶*?@param?objectName?目錄路徑*?@return*/@SneakyThrows(Exception.class)public?ObjectWriteResponse?createDir(String?bucketName,?String?objectName)?{return?minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(new?ByteArrayInputStream(new?byte[]{}),?0,?-1).build());}/***?獲取文件信息,?如果拋出異常則說明文件不存在**?@param?bucketName?存儲桶*?@param?objectName?文件名稱*?@return*/@SneakyThrows(Exception.class)public?String?getFileStatusInfo(String?bucketName,?String?objectName)?{return?minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()).toString();}/***?拷貝文件**?@param?bucketName????存儲桶*?@param?objectName????文件名*?@param?srcBucketName?目標存儲桶*?@param?srcObjectName?目標文件名*/@SneakyThrows(Exception.class)public?ObjectWriteResponse?copyFile(String?bucketName,?String?objectName,?String?srcBucketName,?String?srcObjectName)?{return?minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder().bucket(bucketName).object(objectName).build()).bucket(srcBucketName).object(srcObjectName).build());}/***?刪除文件**?@param?bucketName?存儲桶*?@param?objectName?文件名稱*/@SneakyThrows(Exception.class)public?void?removeFile(String?bucketName,?String?objectName)?{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("[Minio工具類]>>>>?批量刪除文件,異常:",?e);}});}/***?獲取文件外鏈**?@param?bucketName?存儲桶*?@param?objectName?文件名*?@param?expires????過期時間?<=7?秒?(外鏈有效時間(單位:秒))*?@return?url*/@SneakyThrows(Exception.class)public?String?getPresignedObjectUrl(String?bucketName,?String?objectName,?Integer?expires)?{GetPresignedObjectUrlArgs?args?=?GetPresignedObjectUrlArgs.builder().expiry(expires).bucket(bucketName).object(objectName).build();return?minioClient.getPresignedObjectUrl(args);}/***?獲得文件外鏈**?@param?bucketName*?@param?objectName*?@return?url*/@SneakyThrows(Exception.class)public?String?getPresignedObjectUrl(String?bucketName,?String?objectName)?{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");}
}
項目啟動初始化配置
創建配置類InitConfig.java
,并實現了InitializingBean接口
,重寫afterPropertiesSet
方法。
該方法主要實現邏輯:在項目啟動的時候初始化Bucket,如果沒有則進行創建!
InitConfig.java
/***?@program:?xxkfz-minio*?@ClassName?Init.java*?@author:?wust*?@create:?2024-03-16?10:34*?@description:?項目啟動初始化配置**/
@Component
@Slf4jpublic?class?InitConfig?implements?InitializingBean?{@Autowiredprivate?MinioUtils?minioUtils;@Autowiredprivate?MinioConfig?minioConfig;@Overridepublic?void?afterPropertiesSet()?throws?Exception?{//?項目啟動創建Bucket,不存在則進行創建minioUtils.createBucket(minioConfig.getBucketName());}
}
編寫測試接口
MinioController.java
/***?@program:?xxkfz-minio*?@ClassName?OSSController.java*?@author:?wust*?@create:?2024-03-13?11:01*?@description:**/
@Slf4j
@RestController
@RequestMapping("/oss")
public?class?MinioController?{@Autowiredprivate?MinioUtils?minioUtils;@Autowiredprivate?MinioConfig?minioConfig;/***?文件上傳**?@param?file*/@PostMapping("/upload")public?String?upload(@RequestParam("file")?MultipartFile?file)?{try?{//文件名String?fileName?=?file.getOriginalFilename();String?newFileName?=?System.currentTimeMillis()?+?"."?+?StringUtils.substringAfterLast(fileName,?".");//類型String?contentType?=?file.getContentType();minioUtils.uploadFile(minioConfig.getBucketName(),?file,?newFileName,?contentType);return?"上傳成功,文件名:"?+?newFileName;}?catch?(Exception?e)?{e.printStackTrace();return?"上傳失敗";}}/***?刪除**?@param?fileName*/@DeleteMapping("/")public?void?delete(@RequestParam("fileName")?String?fileName)?{minioUtils.removeFile(minioConfig.getBucketName(),?fileName);}/***?獲取文件信息**?@param?fileName*?@return*/@GetMapping("/info")public?String?getFileStatusInfo(@RequestParam("fileName")?String?fileName)?{return?minioUtils.getFileStatusInfo(minioConfig.getBucketName(),?fileName);}/***?獲取文件外鏈**?@param?fileName*?@return*/@GetMapping("/url")public?String?getPresignedObjectUrl(@RequestParam("fileName")?String?fileName)?{return?minioUtils.getPresignedObjectUrl(minioConfig.getBucketName(),?fileName);}/***?文件下載**?@param?fileName*?@param?response*/@GetMapping("/download")public?void?download(@RequestParam("fileName")?String?fileName,?HttpServletResponse?response)?{try?{InputStream?fileInputStream?=?minioUtils.getObject(minioConfig.getBucketName(),?fileName);response.setHeader("Content-Disposition",?"attachment;filename="?+?fileName);response.setContentType("application/force-download");response.setCharacterEncoding("UTF-8");IOUtils.copy(fileInputStream,?response.getOutputStream());}?catch?(Exception?e)?{log.error("下載失敗");}}
}
測試驗證
啟動項目:
上傳圖片
測試接口:http://localhost:8099/oss/upload
進入服務器查看文件上傳情況。
進入目錄:/root/xxkfz/soft/minio/data/xk-admin
當然,也可以直接訪問minio的地址:http://IP地址:9001/xk-admin/1710558001536.jpg。驗證文件是否上傳成功。
獲取文件信息
測試接口:http://localhost:8099/oss/info
獲取文件外鏈
測試接口:http://localhost:8099/oss/url
下載文件
測試接口:http://localhost:8099/oss/download