javascript
SpringBoot整合Minio 项目中使用自己文件存储服务器
前言:基本上每個項目,都會有個上傳文件、頭像這樣的需求,文件可以存儲在阿里云、騰訊云、七牛云這樣的對象存儲服務上,但是使用這些都不能白嫖,這就讓人很難受啊。然后就找到了這個Minio,感覺還是很爽的,全部由自己掌控。代碼中附帶詳細解釋,不懂的也可以留言或私信,會及時作出回復!
地點:湖南省永州市藍山縣舜河村
作者:用心笑*😀
SpringBoot整合Minio 項目中使用自己文件存儲服務器!!!
- 一、前言及環境準備
- 二、項目初始化
- 2.1、新建一個SpringBoot 項目
- 2.2、pom.xml文件
- 2.3、yml文件
- 2.4、完善包結構
- 三、敲代碼(CV大法)
- 3.1、MinioProperties
- 3.2、使用到的工具類
- FileTypeUtils
- MinioUtil
- 3.3、Service層編寫
- MinioService
- MinioServiceImpl
- 3.4、Controller層編寫
- 四、實戰測試
- 4.1、文件上傳
- 4.2、文件下載
- 4.3、其他
- 五、自言自語
一、前言及環境準備
minio介紹: MinIO是根據GNU Affero通用公共許可證v3.0發布的高性能對象存儲。
史上最詳細Docker安裝Minio
minio特點:
- 高性能(讀/寫速度上高達183 GB / 秒 和 171 GB / 秒)
- 可擴展性(擴展從單個群集開始,該群集可以與其他MinIO群集聯合以創建全局名稱空間, 并在需要時可以跨越多個不同的數據中心。)
- 可存儲文件類型多,視頻、execl文件、圖片等等都是可以的。
- 實戰的話 1)文件存儲 2) 數據庫文件備份等
大家都使用過云存儲,minio其實也差不多,只是可以更加的方便。
別看我寫這么多代碼,其實邏輯非常簡單,大家安裝好minio,直接CV大法就能跑了。😀👨?💻
對了,如果你需要找一個判斷文件類型的工具類,此文也涵蓋了。🙆?♂?
環境準備
- 服務器上Docker安裝MInio ?(服務器上Docker安裝Minio)
- 本地下載Minio:minio官網
項目結構
只要搭建好minio服務后,項目編碼實際上特別簡單。
二、項目初始化
2.1、新建一個SpringBoot 項目
我想這個大家都會哈
2.2、pom.xml文件
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.2</version><relativePath/> <!-- lookup parent from repository --> </parent><dependencies><!--此處我用的最近更新的minio jar包--><dependency> <groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></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-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.6.5</version></dependency><!--為了兼容性 我用的是jdk11--><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.0</version></dependency><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-impl</artifactId><version>2.3.0</version></dependency><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-core</artifactId><version>2.3.0</version></dependency><dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency> </dependencies>2.3、yml文件
spring:profiles:active: prod server:port: 8085 spring:application:name: springboot-minio minio:endpoint: http://IP地址 :9000port: 9000accessKey: 登錄賬號secretKey: 登錄密碼secure: falsebucket-name: commons # 桶名 我這是給出了一個默認桶名image-size: 10485760 # 我在這里設定了 圖片文件的最大大小file-size: 1073741824 # 此處是設定了文件的最大大小2.4、完善包結構
大家隨自己習慣哈。(🐕保命)
三、敲代碼(CV大法)
3.1、MinioProperties
存在于config包下,此類的主要作用就是與配置文件進行綁定,方便注入以及后期維護。
import io.minio.MinioClient; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;/*** @author crush*/ @Data @Configuration @ConfigurationProperties(prefix = "minio") public class MinioProperties {/*** 是一個URL,域名,IPv4或者IPv6地址")*/private String endpoint;/*** //"TCP/IP端口號"*/private Integer port;/*** //"accessKey類似于用戶ID,用于唯一標識你的賬戶"*/private String accessKey;/*** //"secretKey是你賬戶的密碼"*/private String secretKey;/*** //"如果是true,則用的是https而不是http,默認值是true"*/private boolean secure;/*** //"默認存儲桶"*/private String bucketName;/*** 圖片的最大大小*/private long imageSize;/*** 其他文件的最大大小*/private long fileSize;/*** 官網給出的 構造方法,我只是去爬了一下官網 (狗頭保命)* 此類是 客戶端進行操作的類*/@Beanpublic MinioClient minioClient() {MinioClient minioClient =MinioClient.builder().credentials(accessKey, secretKey).endpoint(endpoint,port,secure).build();return minioClient;} }3.2、使用到的工具類
FileTypeUtils :是我結合Hutool 工具包 再次封裝的一個工具類,為了方便調用的返回數據。
自己覺得還是挺實用的(👩?🚀🤱)
MinioUtil:是對minioClient操作的再一次封裝。
FileTypeUtils
我是將文件分了大類,然后再根據準確的文件后綴名選擇文件保存方式。
import cn.hutool.core.io.FileTypeUtil; import org.springframework.web.multipart.MultipartFile;import java.io.IOException; import java.io.InputStream;/*** @Author: crush* @Date: 2021-07-25 22:26* version 1.0*/ public class FileTypeUtils {private final static String IMAGE_TYPE = "image/";private final static String AUDIO_TYPE = "audio/";private final static String VIDEO_TYPE = "video/";private final static String APPLICATION_TYPE = "application/";private final static String TXT_TYPE = "text/";public static String getFileType(MultipartFile multipartFile) {InputStream inputStream = null;String type = null;try {inputStream = multipartFile.getInputStream();type = FileTypeUtil.getType(inputStream);System.out.println(type);if (type.equalsIgnoreCase("JPG") || type.equalsIgnoreCase("JPEG")|| type.equalsIgnoreCase("GIF") || type.equalsIgnoreCase("PNG")|| type.equalsIgnoreCase("BMP") || type.equalsIgnoreCase("PCX")|| type.equalsIgnoreCase("TGA") || type.equalsIgnoreCase("PSD")|| type.equalsIgnoreCase("TIFF")) {return IMAGE_TYPE+type;}if (type.equalsIgnoreCase("mp3") || type.equalsIgnoreCase("OGG")|| type.equalsIgnoreCase("WAV") || type.equalsIgnoreCase("REAL")|| type.equalsIgnoreCase("APE") || type.equalsIgnoreCase("MODULE")|| type.equalsIgnoreCase("MIDI") || type.equalsIgnoreCase("VQF")|| type.equalsIgnoreCase("CD")) {return AUDIO_TYPE+type;}if (type.equalsIgnoreCase("mp4") || type.equalsIgnoreCase("avi")|| type.equalsIgnoreCase("MPEG-1") || type.equalsIgnoreCase("RM")|| type.equalsIgnoreCase("ASF") || type.equalsIgnoreCase("WMV")|| type.equalsIgnoreCase("qlv") || type.equalsIgnoreCase("MPEG-2")|| type.equalsIgnoreCase("MPEG4") || type.equalsIgnoreCase("mov")|| type.equalsIgnoreCase("3gp")) {return VIDEO_TYPE+type;}if (type.equalsIgnoreCase("doc") || type.equalsIgnoreCase("docx")|| type.equalsIgnoreCase("ppt") || type.equalsIgnoreCase("pptx")|| type.equalsIgnoreCase("xls") || type.equalsIgnoreCase("xlsx")|| type.equalsIgnoreCase("zip")||type.equalsIgnoreCase("jar")) {return APPLICATION_TYPE+type;}if (type.equalsIgnoreCase("txt")) {return TXT_TYPE+type;}} catch (IOException e) {e.printStackTrace();}return null;} }MinioUtil
這個就比較多了,畢竟是對minioClient 的再次封裝。代碼簡單,你莫慌,直接CV 完慢慢看🧜?♂?
import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse;import com.crush.minio.config.MinioProperties; import io.minio.*; import io.minio.http.Method; import io.minio.messages.DeleteObject; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import io.minio.errors.ErrorResponseException; import io.minio.messages.Bucket; import io.minio.messages.DeleteError; import io.minio.messages.Item; import lombok.SneakyThrows;/*** @Author crush* @Date 2021/7/25 11:43*/@Component public class MinioUtil {private final MinioClient minioClient;private final MinioProperties minioProperties;public MinioUtil(MinioClient minioClient, MinioProperties minioProperties) {this.minioClient = minioClient;this.minioProperties = minioProperties;}/*** 檢查存儲桶是否存在** @param bucketName 存儲桶名稱* @return*/@SneakyThrowspublic boolean bucketExists(String bucketName) {boolean found =minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if (found) {System.out.println(bucketName + " exists");} else {System.out.println(bucketName + " does not exist");}return found;}/*** 創建存儲桶** @param bucketName 存儲桶名稱*/@SneakyThrowspublic boolean makeBucket(String bucketName) {boolean flag = bucketExists(bucketName);if (!flag) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());return true;} else {return false;}}/*** 列出所有存儲桶名稱** @return*/@SneakyThrowspublic List<String> listBucketNames() {List<Bucket> bucketList = listBuckets();List<String> bucketListName = new ArrayList<>();for (Bucket bucket : bucketList) {bucketListName.add(bucket.name());}return bucketListName;}/*** 列出所有存儲桶** @return*/@SneakyThrowspublic List<Bucket> listBuckets() {return minioClient.listBuckets();}/*** 刪除存儲桶** @param bucketName 存儲桶名稱* @return*/@SneakyThrowspublic boolean removeBucket(String bucketName) {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(RemoveBucketArgs.builder().bucket(bucketName).build());flag = bucketExists(bucketName);if (!flag) {return true;}}return false;}/*** 列出存儲桶中的所有對象名稱** @param bucketName 存儲桶名稱* @return*/@SneakyThrowspublic List<String> listObjectNames(String bucketName) {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());}}else{listObjectNames.add("存儲桶不存在");}return listObjectNames;}/*** 列出存儲桶中的所有對象** @param bucketName 存儲桶名稱* @return*/@SneakyThrowspublic Iterable<Result<Item>> listObjects(String bucketName) {boolean flag = bucketExists(bucketName);if (flag) {return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());}return null;}/*** 文件上傳** @param bucketName* @param multipartFile*/@SneakyThrowspublic void putObject(String bucketName, MultipartFile multipartFile, String filename, String fileType) {InputStream inputStream = new ByteArrayInputStream(multipartFile.getBytes());minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(filename).stream(inputStream, -1, minioProperties.getFileSize()).contentType(fileType).build());}/*** 文件訪問路徑** @param bucketName 存儲桶名稱* @param objectName 存儲桶里的對象名稱* @return*/@SneakyThrowspublic String getObjectUrl(String bucketName, String objectName) {boolean flag = bucketExists(bucketName);String url = "";if (flag) {url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectName).expiry(2, TimeUnit.MINUTES).build());System.out.println(url);}return url;}/*** 刪除一個對象** @param bucketName 存儲桶名稱* @param objectName 存儲桶里的對象名稱*/@SneakyThrowspublic boolean removeObject(String bucketName, String objectName) {boolean flag = bucketExists(bucketName);if (flag) {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());return true;}return false;}/*** 以流的形式獲取一個文件對象** @param bucketName 存儲桶名稱* @param objectName 存儲桶里的對象名稱* @return*/@SneakyThrowspublic InputStream getObject(String bucketName, String objectName) {boolean flag = bucketExists(bucketName);if (flag) {StatObjectResponse statObject = statObject(bucketName, objectName);if (statObject != null && statObject.size() > 0) {InputStream stream =minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());return stream;}}return null;}/*** 獲取對象的元數據** @param bucketName 存儲桶名稱* @param objectName 存儲桶里的對象名稱* @return*/@SneakyThrowspublic StatObjectResponse statObject(String bucketName, String objectName) {boolean flag = bucketExists(bucketName);if (flag) {StatObjectResponse stat =minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());return stat;}return null;}/*** 刪除指定桶的多個文件對象,返回刪除錯誤的對象列表,全部刪除成功,返回空列表** @param bucketName 存儲桶名稱* @param objectNames 含有要刪除的多個object名稱的迭代器對象* @return*/@SneakyThrowspublic boolean removeObject(String bucketName, List<String> objectNames) {boolean flag = bucketExists(bucketName);if (flag) {List<DeleteObject> objects = new LinkedList<>();for (int i = 0; i < objectNames.size(); i++) {objects.add(new DeleteObject(objectNames.get(i)));}Iterable<Result<DeleteError>> results =minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());for (Result<DeleteError> result : results) {DeleteError error = result.get();System.out.println("Error in deleting object " + error.objectName() + "; " + error.message());return false;}}return true;}/*** 以流的形式獲取一個文件對象(斷點下載)** @param bucketName 存儲桶名稱* @param objectName 存儲桶里的對象名稱* @param offset 起始字節的位置* @param length 要讀取的長度 (可選,如果無值則代表讀到文件結尾)* @return*/@SneakyThrowspublic InputStream getObject(String bucketName, String objectName, long offset, Long length) {boolean flag = bucketExists(bucketName);if (flag) {StatObjectResponse statObject = statObject(bucketName, objectName);if (statObject != null && statObject.size() > 0) {InputStream stream =minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length).build());return stream;}}return null;}/*** 通過InputStream上傳對象** @param bucketName 存儲桶名稱* @param objectName 存儲桶里的對象名稱* @param inputStream 要上傳的流* @param contentType 要上傳的文件類型 MimeTypeUtils.IMAGE_JPEG_VALUE* @return*/@SneakyThrowspublic boolean putObject(String bucketName, String objectName, InputStream inputStream,String contentType) {boolean flag = bucketExists(bucketName);if (flag) {minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(inputStream, -1, minioProperties.getFileSize()).contentType(contentType).build());StatObjectResponse statObject = statObject(bucketName, objectName);if (statObject != null && statObject.size() > 0) {return true;}}return false;} }3.3、Service層編寫
MinioService
import io.minio.messages.Bucket; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.util.List;/*** @Author crush* @Date 2021/7/25 9:58* @Description: MinioService*/ public interface MinioService {/*** 判斷 bucket是否存在** @param bucketName* @return*/boolean bucketExists(String bucketName);/*** 創建 bucket** @param bucketName*/void makeBucket(String bucketName);/*** 列出所有存儲桶名稱* @return*/List<String> listBucketName();/*** 列出所有存儲桶 信息** @return*/List<Bucket> listBuckets();/*** 根據桶名刪除桶* @param bucketName*/boolean removeBucket(String bucketName);/*** 列出存儲桶中的所有對象名稱* @param bucketName* @return*/List<String> listObjectNames(String bucketName);/*** 文件上傳** @param multipartFile* @param bucketName*/String putObject( MultipartFile multipartFile, String bucketName,String fileType);/*** 文件流下載* @param bucketName* @param objectName* @return*/InputStream downloadObject(String bucketName, String objectName);/*** 刪除文件* @param bucketName* @param objectName*/boolean removeObject(String bucketName, String objectName);/*** 批量刪除文件* @param bucketName* @param objectNameList* @return*/boolean removeListObject(String bucketName, List<String> objectNameList);/*** 獲取文件路徑* @param bucketName* @param objectName* @return*/String getObjectUrl(String bucketName,String objectName); }MinioServiceImpl
import com.crush.minio.config.MinioProperties; import com.crush.minio.service.MinioService; import com.crush.minio.utils.MinioUtil; import io.minio.MinioClient; import io.minio.messages.Bucket; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile;import java.io.InputStream; import java.util.List; import java.util.UUID;/*** @Author crush* @Date 2021/7/25 9:58* @Description: MinioServiceImpl*/ @Service public class MinioServiceImpl implements MinioService {private final MinioUtil minioUtil;private final MinioClient minioClient;private final MinioProperties minioProperties;public MinioServiceImpl(MinioUtil minioUtil, MinioClient minioClient, MinioProperties minioProperties) {this.minioUtil = minioUtil;this.minioClient = minioClient;this.minioProperties = minioProperties;}@Overridepublic boolean bucketExists(String bucketName) {return minioUtil.bucketExists(bucketName);}@Overridepublic void makeBucket(String bucketName) {minioUtil.makeBucket(bucketName);}@Overridepublic List<String> listBucketName() {return minioUtil.listBucketNames();}@Overridepublic List<Bucket> listBuckets() {return minioUtil.listBuckets();}@Overridepublic boolean removeBucket(String bucketName) {return minioUtil.removeBucket(bucketName);}@Overridepublic List<String> listObjectNames(String bucketName) {return minioUtil.listObjectNames(bucketName);}@Overridepublic String putObject(MultipartFile file, String bucketName,String fileType) {try {bucketName = StringUtils.isNotBlank(bucketName) ? bucketName : minioProperties.getBucketName();if (!this.bucketExists(bucketName)) {this.makeBucket(bucketName);}String fileName = file.getOriginalFilename();String objectName = UUID.randomUUID().toString().replaceAll("-", "")+ fileName.substring(fileName.lastIndexOf("."));minioUtil.putObject(bucketName, file, objectName,fileType);return minioProperties.getEndpoint()+"/"+bucketName+"/"+objectName;} catch (Exception e) {e.printStackTrace();return "上傳失敗";}}@Overridepublic InputStream downloadObject(String bucketName, String objectName) {return minioUtil.getObject(bucketName,objectName);}@Overridepublic boolean removeObject(String bucketName, String objectName) {return minioUtil.removeObject(bucketName, objectName);}@Overridepublic boolean removeListObject(String bucketName, List<String> objectNameList) {return minioUtil.removeObject(bucketName,objectNameList);}@Overridepublic String getObjectUrl(String bucketName,String objectName) {return minioUtil.getObjectUrl(bucketName, objectName);} }3.4、Controller層編寫
import com.crush.minio.service.MinioService; import com.crush.minio.utils.FileTypeUtils; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.List; import java.util.Map;/*** @author crush*/ @RequestMapping("/minio") @RestController public class MinioController {private final MinioService minioService;public MinioController(MinioService minioService) {this.minioService = minioService;}@PostMapping("/upload")public String uploadFile(MultipartFile file, String bucketName) {String fileType = FileTypeUtils.getFileType(file);if (fileType != null) {return minioService.putObject(file, bucketName, fileType);}return "不支持的文件格式。請確認格式,重新上傳!!!";}@PostMapping("/addBucket/{bucketName}")public String addBucket(@PathVariable String bucketName) {minioService.makeBucket(bucketName);return "創建成功!!!";}@GetMapping("/show/{bucketName}")public List<String> show(@PathVariable String bucketName) {return minioService.listObjectNames(bucketName);}@GetMapping("/showBucketName")public List<String> showBucketName() {return minioService.listBucketName();}@GetMapping("/showListObjectNameAndDownloadUrl/{bucketName}")public Map<String, String> showListObjectNameAndDownloadUrl(@PathVariable String bucketName) {Map<String, String> map = new HashMap<>();List<String> listObjectNames = minioService.listObjectNames(bucketName);String url = "localhost:8085/minio/download/" + bucketName + "/";listObjectNames.forEach(System.out::println);for (int i = 0; i <listObjectNames.size() ; i++) {map.put(listObjectNames.get(i),url+listObjectNames.get(i));}return map;}@DeleteMapping("/removeBucket/{bucketName}")public String delBucketName(@PathVariable String bucketName) {return minioService.removeBucket(bucketName) == true ? "刪除成功" : "刪除失敗";}@DeleteMapping("/removeObject/{bucketName}/{objectName}")public String delObject(@PathVariable("bucketName") String bucketName, @PathVariable("objectName") String objectName) {return minioService.removeObject(bucketName, objectName) == true ? "刪除成功" : "刪除失敗";}@DeleteMapping("/removeListObject/{bucketName}")public String delListObject(@PathVariable("bucketName") String bucketName, @RequestBody List<String> objectNameList) {return minioService.removeListObject(bucketName, objectNameList) == true ? "刪除成功" : "刪除失敗";}@RequestMapping("/download/{bucketName}/{objectName}")public void download(HttpServletResponse response, @PathVariable("bucketName") String bucketName, @PathVariable("objectName") String objectName) {InputStream in = null;try {in = minioService.downloadObject(bucketName, objectName);response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(objectName, "UTF-8"));response.setCharacterEncoding("UTF-8");//將字節從InputStream復制到OutputStream 。IOUtils.copy(in, response.getOutputStream());} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (in != null) {try {in.close();} catch (IOException e) {e.printStackTrace();}}}} }主啟動沒啥要改的,直接跑就歐克拉
莫慌,竟然帶大家做了,肯定是要帶大家看看測試結果的。
👇
四、實戰測試
我目前Minio 的所含有的桶
4.1、文件上傳
在可視化平臺上也可以看到已經上傳成功了。
4.2、文件下載
這個就是文件下載接口。
4.3、其他
其他的沒有一一測試,但是方法命名應該可以給予你提示。
五、自言自語
如若遇到錯誤或疑惑之處,請留言或私信,會及時給予回復。
Java這條路啊,真是越往前越卷啊。🛌
源碼地址:gitee
總結
以上是生活随笔為你收集整理的SpringBoot整合Minio 项目中使用自己文件存储服务器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你了解过Spring支持的常用数据库事务
- 下一篇: SpringBoot整合Security