小伙伴們好,歡迎關(guān)注,一起學(xué)習(xí),無(wú)限進(jìn)步
minio 是對(duì)象存儲(chǔ)服務(wù)。它基于 Apache License 開(kāi)源協(xié)議,兼容 Amazon S3 云存儲(chǔ)接口。適合存儲(chǔ)非結(jié)構(gòu)化數(shù)據(jù),如圖片,音頻,視頻,日志等。對(duì)象文件最大可以達(dá)到 5TB。
優(yōu)點(diǎn)有高性能,可擴(kuò)展,操作簡(jiǎn)單,有圖形化操作界面,讀寫(xiě)性能優(yōu)異等。官網(wǎng)
minio 部署可參考這篇:Minio 詳細(xì)安裝部署步驟
SpringBoot 快速整合 minio
1、添加 Maven 依賴
在 pom.xml
文件中添加MinIO客戶端依賴項(xiàng)
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.2</version>
</dependency>
<!-- 如果 minio 版本是8.3.4以上 okhttp 一定要大于 4.8.1 版本 -->
<!-- <dependency>-->
<!-- <groupId>com.squareup.okhttp3</groupId>-->
<!-- <artifactId>okhttp</artifactId>-->
<!-- <version>4.8.2</version>-->
<!-- </dependency>-->
<!-- 還用到了 fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
2、配置MinIO連接信息
在 application.properties
或 application.yml
文件中配置MinIO的連接信息,包括服務(wù)器地址、端口、憑據(jù)等信息
# Minio 配置
minio.endpoint=127.0.0.1:9000 #對(duì)象存儲(chǔ)服務(wù)的URL
minio.accessKey=admin #Access key賬戶 寫(xiě)賬號(hào)也可以
minio.secretKey=admin #Secret key密碼
minio.bucketName=test # 桶名稱
# 過(guò)期時(shí)間
minio.expire=7200
# Minio 配置
minio:
endpoint: 127.0.0.1:9000 #對(duì)象存儲(chǔ)服務(wù)的URL
accessKey: admin #Access key賬戶 寫(xiě)賬號(hào)也可以
secretKey: admin #Secret key密碼
bucketName: test # 桶名稱
expire: 7200 # 過(guò)期時(shí)間
3、創(chuàng)建 MinIO 客戶端 Bean
在 SpringBoot 應(yīng)用的配置類中創(chuàng)建一個(gè) MinIO客戶端的 Bean,以便在應(yīng)用中使用 MinIO 服務(wù)
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* minio 配置屬性
*/
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
/**
* Minio 連接地址
*/
private String endpoint;
/**
* accessKey 或 賬號(hào)
*/
private String accessKey;
/**
* secretKey 或 密碼
*/
private String secretKey;
/**
* 桶名稱
*/
private String bucketName;
/**
* 默認(rèn)是秒 地址過(guò)期時(shí)間,設(shè)置默認(rèn)值7200秒
*/
private int expire = 7200;
}
4、異常枚舉類
/**
* 異常枚舉類
*/
public enum ExceptionEnums {
FILE_NAME_NOT_NULL("0001", "文件名不能為空"),
BUCKET_NAME_NOT_NULL("0002", "桶名稱不能為空"),
FILE_NOT_EXIST("0003", "文件不存在"),
BUCKET_NOT_EXIST("0004", "桶不存在"),
BUCKET_NAME_NOT_EXIST("0005", "桶不存在,需要先創(chuàng)建桶在創(chuàng)建文件夾");//枚舉類如果寫(xiě)方法的話,此處需要寫(xiě)分號(hào)
private String code;
private String msg;
ExceptionEnums(String ecode, String emsg) {
this.code = ecode;
this.msg = emsg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
public static ExceptionEnums statOf(String code) {
for (ExceptionEnums state : values())
if (state.getCode().equals(code))
return state;
return null;
}
}
5、全局異常
import org.springframework.http.HttpStatus;
/**
* 異常
*/
public class GeneralException extends RuntimeException {
private Integer errorCode;
public GeneralException() {
}
public GeneralException(Throwable throwable) {
super(throwable);
}
public GeneralException(String msg) {
super(msg);
this.errorCode = HttpStatus.INTERNAL_SERVER_ERROR.value();
}
public GeneralException(Integer errorCode, String msg) {
super(msg);
this.errorCode = errorCode;
}
public Integer getErrorCode() {
return this.errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
}
6、minio 工具類
工具類包含創(chuàng)建 bucket,獲取全部 bucket,獲取 bucket 文件名和大小列表,文件上傳,獲取上傳文件的完整路徑,創(chuàng)建文件夾或目錄,判斷 bucket 是否存在,判斷文件是否存在,文件下載,刪除文件,批量刪除文件方法文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-859184.html
import com.alibaba.fastjson.JSON;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.*;
/**
* Minio 工具類
*/
@Component
@Slf4j
public class MinioUtils {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioProperties minioProperties;
/**
* 初始化Bucket
*/
private void createBucket(String bucketName) {
try {
// 判斷 BucketName 是否存在
if (bucketExists(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 驗(yàn)證bucketName是否存在
*
* @return boolean true:存在
*/
public boolean bucketExists(String bucketName) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
boolean flag = true;
try {
flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* 獲取全部bucket
* <p>
*/
public List<String> getAllBuckets() {
List<String> list = null;
try {
final List<Bucket> buckets = minioClient.listBuckets();
list = new ArrayList<>(buckets.size());
for (Bucket bucket : buckets) {
list.add(bucket.name());
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
/**
* 根據(jù)bucketName獲取信息
*
* @param bucketName bucket名稱
* @return
*/
public String getBucket(String bucketName) throws Exception {
final Optional<Bucket> first = minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
String name = null;
if (first.isPresent()) {
name = first.get().name();
}
return name;
}
/**
* 獲取桶中文件名和大小列表
*
* @param bucketName bucket名稱
* @param recursive 查詢是否遞歸
* @return
*/
public List<Object> getFileList(String bucketName, boolean recursive) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
List<Object> items = new ArrayList<>();
try {
Iterable<Result<Item>> myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix("/2022-08-03/4674a894-abaf-48cb-9ea9-40a4e8560af9/Desktop").recursive(recursive).build());
Iterator<Result<Item>> iterator = myObjects.iterator();
String format = "{'fileName':'%s','fileSize':'%s'}";
for (Result<Item> myObject : myObjects) {
System.out.println(myObject.get().objectName());
}
while (iterator.hasNext()) {
Item item = iterator.next().get();
items.add(JSON.parse(String.format(format, item.objectName(), formatFileSize(item.size()))));
// items.add(JSON.parse(String.format(format, "/".concat("test").concat("/").concat(item.objectName()), formatFileSize(item.size()))));
}
} catch (Exception e) {
e.printStackTrace();
log.info(e.getMessage());
}
items.remove(0);
return items;
}
/**
* 文件上傳
*
* @param file
* @return
*/
public Map<String, Object> uploadFile(String bucketName, MultipartFile[] file) {
if (file == null || file.length == 0) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
List<String> orgfileNameList = new ArrayList<>(file.length);
for (MultipartFile multipartFile : file) {
String orgfileName = multipartFile.getOriginalFilename();
orgfileNameList.add(orgfileName);
try {
//文件上傳
InputStream in = multipartFile.getInputStream();
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(orgfileName).stream(in, multipartFile.getSize(), -1).contentType(multipartFile.getContentType()).build());
in.close();
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
Map<String, Object> data = new HashMap<>();
data.put("bucketName", bucketName);
data.put("fileName", orgfileNameList);
return data;
}
/**
* 獲取上傳文件的完整路徑
*
* @param bucketName 桶名稱
* @param fileName 文件名
* @param expire 地址過(guò)期時(shí)間
* @return
*/
public String getPresignedObjectUrl(String bucketName, String fileName, Integer expire) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(fileName)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
expire = Objects.isNull(expire) ? minioProperties.getExpire() : expire;
// 驗(yàn)證桶是否存在在
final boolean validationBucket = bucketExists(bucketName);
if (!validationBucket) {
throw new GeneralException(ExceptionEnums.BUCKET_NOT_EXIST.getMsg());
}
// 驗(yàn)證文件是否存在
final boolean validationFileName = doFileNameExist(bucketName, fileName);
if (!validationFileName) {
throw new GeneralException(ExceptionEnums.FILE_NOT_EXIST.getMsg());
}
String url = null;
try {
// 獲取桶和文件的完整路徑
url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).method(Method.GET).expiry(expire).build());
} catch (MinioException e) {
log.error("Error occurred: " + e);
} catch (Exception e) {
e.printStackTrace();
}
return url;
}
/**
* 創(chuàng)建文件夾或目錄
*
* @param bucketName 存儲(chǔ)桶
* @param objectName 目錄路徑
*/
public Map<String, String> putDirObject(String bucketName, String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
// 判斷桶是否存在
if (!bucketExists(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_EXIST.getMsg());
}
final ObjectWriteResponse response = minioClient.putObject(
PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
new ByteArrayInputStream(new byte[]{}), 0, -1)
.build());
Map<String, String> map = new HashMap<>();
map.put("etag", response.etag());
map.put("versionId", response.versionId());
return map;
}
/**
* 判斷桶是否存在
*
* @param bucketName 存儲(chǔ)桶
* @param objectName 文件夾名稱(去掉/)
* @return true:存在
*/
public boolean doFolderExist(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()) {
exist = true;
}
}
} catch (Exception e) {
exist = false;
}
return exist;
}
/**
* 判斷文件是否存在
*
* @param fileName 對(duì)象
* @return true:存在
*/
public boolean doFileNameExist(String bucketName, String fileName) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(fileName)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
boolean exist = true;
try {
minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
} catch (Exception e) {
exist = false;
}
return exist;
}
/**
* 文件下載
*
* @param response
* @param fileName
*/
public void downloadFile(HttpServletResponse response, String bucketName, String fileName) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(fileName)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
// 判斷文件是否存在
final boolean flag = doFileNameExist(bucketName, fileName);
if (!flag) {
throw new GeneralException(ExceptionEnums.FILE_NOT_EXIST.getMsg());
}
InputStream in = null;
try {
// 獲取對(duì)象信息
StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
response.setContentType(stat.contentType());
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
//文件下載
in = minioClient.getObject(
GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
IOUtils.copy(in, response.getOutputStream());
} catch (Exception e) {
log.error(e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
}
/**
* 刪除文件
*
* @param bucketName bucket名稱
* @param fileName 文件名稱
* 說(shuō)明:當(dāng)前方法不能真正刪除,需要驗(yàn)證
*/
public void deleteFile(String bucketName, String fileName) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(fileName)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
try {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
}
}
/**
* 批量文件刪除
*
* @param bucketName bucket名稱
* @param fileNames 文件名
*/
public void deleteBatchFile(String bucketName, List<String> fileNames) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (CollectionUtils.isEmpty(fileNames)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
try {
List<DeleteObject> objects = new LinkedList<>();
for (String fileName : fileNames) {
objects.add(new DeleteObject(fileName));
}
Iterable<Result<DeleteError>> results =
minioClient.removeObjects(
RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
log.error("Error occurred: " + error);
}
} catch (Exception e) {
e.printStackTrace();
log.error("批量刪除失?。rror:{}", e);
}
}
/**
* 文件大小
*
* @param fileS
* @return
*/
private static String formatFileSize(long fileS) {
DecimalFormat df = new DecimalFormat("#.00");
String fileSizeString = "";
String wrongSize = "0B";
if (fileS == 0) {
return wrongSize;
}
if (fileS < 1024) {
fileSizeString = df.format((double) fileS) + " B";
} else if (fileS < 1048576) {
fileSizeString = df.format((double) fileS / 1024) + " KB";
} else if (fileS < 1073741824) {
fileSizeString = df.format((double) fileS / 1048576) + " MB";
} else {
fileSizeString = df.format((double) fileS / 1073741824) + " GB";
}
return fileSizeString;
}
}
7、文件調(diào)用接口
上傳文件,獲取上傳文件完整路徑,文件下載,文件刪除,批量刪除文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-859184.html
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
@RestController
public class MinioController {
@Autowired
private MinioUtils minioUtils;
/**
* 獲取桶中文件名和大小列表
*
* @return
*/
@GetMapping("/getFileList")
public List<Object> getFileList() {
return minioUtils.getFileList("test", true);
}
/**
* 判斷文件是否存在
*
* @param bucketName
* @param fileName
* @return
*/
@GetMapping("/doFileNameExist")
public boolean doFileNameExist(String bucketName, String fileName) {
return minioUtils.doFolderExist(bucketName, fileName);
}
/**
* 上傳文件
*
* @param file
* @return
*/
@PostMapping("/uploadFiles")
public Map<String, Object> uploadFiles(String bucketName, @RequestParam(name = "file", required = false) MultipartFile[] file) {
if (file == null || file.length == 0) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
return minioUtils.uploadFile(bucketName, file);
}
/**
* 獲取上傳文件的完整瀏覽路徑
*
* @param filename
* @return
*/
@GetMapping("/getPresignedObjectUrl")
public String getPresignedObjectUrl(@RequestParam(name = "filename") String filename) {
return minioUtils.getPresignedObjectUrl("test", filename, null);
}
/**
* 文件下載
*
* @param response
* @param fileName
*/
@GetMapping("/downloadFile")
public void downloadFile(HttpServletResponse response, @RequestParam("fileName") String fileName) {
minioUtils.downloadFile(response, "test", fileName);
}
/**
* 刪除單個(gè)文件
*
* @param fileName 完整路徑(不包含bucket)
*/
@DeleteMapping("/deleteFile")
public void deleteFile(String bucketName, String fileName) {
minioUtils.deleteFile(bucketName, fileName);
}
/**
* 批量刪除文件
*
* @param fileNames 完整路徑(不包含bucket)
*/
@DeleteMapping("/deleteBatchFile")
public void deleteBatchFile(String bucketName, @RequestParam("fileNames") List<String> fileNames) {
minioUtils.deleteBatchFile(bucketName, fileNames);
}
}
到了這里,關(guān)于SpringBoot整合Minio的詳細(xì)步驟的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!