oss物件儲存-minio初體驗
官方文件地址
https://docs.min.io/
http://docs.minio.org.cn/docs/
JavaSDK:https://github.com/minio/minio-java
什麼是 MinIO?
Minio 是個基於 Golang 編寫的開源物件儲存套件,基於Apache License v2.0開源協議,雖然輕量,卻擁有著不錯的效能。它相容亞馬遜S3雲端儲存服務介面。可以很簡單的和其他應用結合使用,例如 NodeJS、Redis、MySQL等。
MinIO 的應用場景
MinIO 的應用場景除了可以作為私有云的物件儲存服務來使用,也可以作為雲物件儲存的閘道器層,無縫對接Amazon S3
MicroSoft Azure
。
minio特點
-
高效能:
作為一款高效能儲存,在標準硬體條件下,其讀寫速率分別可以達到55Gb/s
和35Gb/s
。並而 MinIO 支援一個物件檔案可以是任意大小,從幾kb到最大5T不等。 -
可擴充套件:
不同MinIO叢集可以組成聯邦,並形成一個全域性的名稱空間,並且支援跨越多個數據中心。 -
雲原生:
容器化、基於K8S的編排、多租戶支援。 -
Amazon S3相容:
使用 Amazon S3 v2 / v4 API。可以使用Minio SDK,Minio Client,AWS SDK 和 AWS CLI 訪問Minio伺服器。 -
可對接多種後端儲存:
除了Minio自己的檔案系統,還支援 DAS、 JBODs、NAS、Google雲端儲存和 Azure Blob儲存。
window安裝minio服務(本地)
1.下載地址
https://dl.min.io/server/minio/release/windows-amd64/minio.exe
2.進入下載目錄,呼叫cmd視窗,並執行如下命令:
.\minio.exe server F:\minio\photos
3.訪問:http://127.0.0.1:9000/,進入minio伺服器登入頁面
預設賬號密碼均為:minioadmin
minio之java應用
1.pom引入jar包依賴
<!-- minio 檔案服務客戶端 --> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>6.0.11</version> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.1.1</version> </dependency>
2.新增配置檔案
- url:minio伺服器的介面地址,不是訪問地址
- accessKey/secretKey:登入minio系統,新建Service Accounts
config: minio: url: http://192.168.1.114:9000 accessKey: MMXHB6HAXWCCVG8ULAVK secretKey: 7J7v99N0LubbzshG2UmOrID9j2PNMG5mpCnBENv1
3.註冊MinioClient
package cn.cjq.config; import io.minio.MinioClient; import lombok.Data; import lombok.SneakyThrows; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; //import org.springframework.cloud.context.config.annotation.RefreshScope; @Data @Configuration @ConfigurationProperties(prefix = "config.minio") public class MinioConfig { /** * minio 服務地址 http://ip:port */ private String url; /** * 使用者名稱 */ private String accessKey; /** * 密碼 */ private String secretKey; @SneakyThrows @Bean // @RefreshScope public MinioClient minioClient(){ return new MinioClient(url, accessKey, secretKey); } }
4.minio工具類
package cn.cjq.util; import cn.cjq.entity.MinioItem; import io.minio.MinioClient; import io.minio.ObjectStat; import io.minio.Result; import io.minio.messages.Bucket; import io.minio.messages.Item; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * minio工具類 * * @author lvlinguang * @date 2022-01-07 12:26 * @see “http://docs.minio.org.cn/docs/master/java-client-api-reference” */ @Component public class MinioUtils { @Autowired private MinioClient client; /** * 建立bucket * * @param bucketName */ @SneakyThrows public void createBucket(String bucketName) { if (!client.bucketExists(bucketName)) { client.makeBucket(bucketName); } } /** * 獲取所有bucket * * @return */ @SneakyThrows public List<Bucket> listAllBuckets() { return client.listBuckets(); } /** * bucket詳情 * * @param bucketName 名稱 * @return */ @SneakyThrows public Optional<Bucket> getBucket(String bucketName) { return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst(); } /** * 刪除bucket * * @param bucketName 名稱 */ @SneakyThrows public void removeBucket(String bucketName) { client.removeBucket(bucketName); } /** * 上傳檔案 * * @param bucketName bucket名稱 * @param objectName 檔名稱 * @param stream 檔案流 * @throws Exception */ @SneakyThrows public void uploadFile(String bucketName, String objectName, InputStream stream) throws Exception { this.uploadFile(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream"); } /** * 上傳檔案 * * @param bucketName bucket名稱 * @param objectName 檔名稱 * @param stream 檔案流 * @param size 大小 * @param contextType 型別 * @throws Exception */ @SneakyThrows public void uploadFile(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception { //如果bucket不存在,則建立 this.createBucket(bucketName); client.putObject(bucketName, objectName, stream, size, null, null, contextType); } /** * 獲取檔案 * * @param bucketName bucket名稱 * @param objectName 檔名稱 * @return */ @SneakyThrows public InputStream getFile(String bucketName, String objectName) { return client.getObject(bucketName, objectName); } /** * 根據檔案前置查詢檔案 * * @param bucketName bucket名稱 * @param prefix 字首 * @param recursive 是否遞迴查詢 * @return */ @SneakyThrows public List<MinioItem> listAllFileByPrefix(String bucketName, String prefix, boolean recursive) { List<MinioItem> objectList = new ArrayList<>(); Iterable<Result<Item>> objectsIterator = client .listObjects(bucketName, prefix, recursive); while (objectsIterator.iterator().hasNext()) { objectList.add(new MinioItem(objectsIterator.iterator().next().get())); } return objectList; } /** * 刪除檔案 * * @param bucketName bucket名稱 * @param objectName 檔名稱 */ @SneakyThrows public void removeFile(String bucketName, String objectName) { client.removeObject(bucketName, objectName); } /** * 獲取檔案外鏈 * * @param bucketName bucket名稱 * @param objectName 檔名稱 * @param expires 過期時間 <=7 * @return */ @SneakyThrows public String getFileURL(String bucketName, String objectName, Integer expires) { return client.presignedGetObject(bucketName, objectName, expires); } /** * 獲取檔案資訊 * * @param bucketName bucket名稱 * @param objectName 檔名稱 * @return */ @SneakyThrows public ObjectStat getFileInfo(String bucketName, String objectName) { return client.statObject(bucketName, objectName); } }View Code
5.檔案操作類
package cn.cjq.util; import cn.cjq.entity.MinioObject; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.time.LocalDate; import java.util.UUID; /** * 系統檔案工具類 * * @author lvlinguang * @date 2021-02-28 12:30 */ @Component public class SysFileUtils { /** * 檔案伺服器中的目錄分隔符 */ public static final String SEPRETOR = "/"; @Autowired private MinioUtils minioUtils; /** * 檔案上傳 * * @param object 檔案對你 * @param bucketName bucket名稱 * @return */ @SneakyThrows public MinioObject uploadFile(MultipartFile object, String bucketName) { return this.uploadFile(object.getInputStream(), bucketName, object.getOriginalFilename()); } /** * 檔案上傳 * * @param object 檔案對你 * @param bucketName bucket名稱 * @param fileName 檔名 * @return */ @SneakyThrows public MinioObject uploadFile(MultipartFile object, String bucketName, String fileName) { return this.uploadFile(object.getInputStream(), bucketName, fileName); } /** * 檔案上傳 * * @param object 檔案對你 * @param bucketName bucket名稱 * @param randomFileName 檔名是否隨機(是:按年/月/日/隨機值儲存,否:按原檔名儲存) * @return */ @SneakyThrows public MinioObject uploadFile(MultipartFile object, String bucketName, Boolean randomFileName) { //檔名 String fileName = object.getOriginalFilename(); if (randomFileName) { //副檔名 String extName = FileUtil.extName(object.getOriginalFilename()); if (StrUtil.isNotBlank(extName)) { extName = StrUtil.DOT + extName; } //新檔名 fileName = randomFileName(extName); } return this.uploadFile(object.getInputStream(), bucketName, fileName); } /** * 檔案上傳 * * @param object 檔案對你 * @param bucketName bucket名稱 * @param fileName 檔名 * @return */ @SneakyThrows public MinioObject uploadFile(InputStream object, String bucketName, String fileName) { try { minioUtils.uploadFile(bucketName, fileName, object); return new MinioObject(minioUtils.getFileInfo(bucketName, fileName)); } catch (Exception e) { throw new Exception(e); } finally { if (object != null) { object.close(); } } } /** * 下載檔案 * * @param response response * @param url 檔案地址(/bucketName/fileName) */ public void downloadFile(HttpServletResponse response, String url) { final String bucketName = getBucketName(url); final String filePath = getFilePath(url); this.downloadFile(response, bucketName, filePath); } /** * 下載檔案 * * @param response response * @param bucket bucket名稱 * @param fileName 檔名 */ public void downloadFile(HttpServletResponse response, String bucket, String fileName) { try (InputStream inputStream = minioUtils.getFile(bucket, fileName)) { if ("jpg".equals(FileUtil.extName(fileName))) { response.setContentType("image/jpeg"); } else if ("png".equals(FileUtil.extName(fileName))) { response.setContentType("image/png"); } else { response.setContentType("application/octet-stream; charset=UTF-8"); } IoUtil.copy(inputStream, response.getOutputStream()); } catch (Exception e) { e.printStackTrace(); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); } } /** * 獲取連結地址的 檔名 * * @param bucketFileUrl * @return */ public static String getFilePath(String bucketFileUrl) { if (bucketFileUrl == null) { return null; } //去掉第一個分割符 if (bucketFileUrl.startsWith(SEPRETOR)) { bucketFileUrl = bucketFileUrl.substring(1); } return bucketFileUrl.substring(bucketFileUrl.indexOf(SEPRETOR) + 1); } /** * 獲取連結地址的 bucketName * * @param bucketFileUrl 地址(/{bucketName}/{path}/{fileName}) * @return */ public static String getBucketName(String bucketFileUrl) { if (bucketFileUrl == null) { return null; } //去掉第一個分割符 if (bucketFileUrl.startsWith(SEPRETOR)) { bucketFileUrl = bucketFileUrl.substring(1); } return bucketFileUrl.substring(0, bucketFileUrl.indexOf(SEPRETOR)); } /** * 生成新的檔名 * * @param extName 副檔名 * @return */ public static String randomFileName(String extName) { LocalDate now = LocalDate.now(); return now.getYear() + SEPRETOR + getFullNumber(now.getMonthValue()) + SEPRETOR + getFullNumber(now.getDayOfMonth()) + SEPRETOR + UUID.randomUUID().toString().replace("-", "") + extName; } /** * 得到數字全稱,帶0 * * @param number * @return */ public static String getFullNumber(Integer number) { if (number < 10) { return "0" + number; } return number.toString(); } }View Code
6.相關實體類
package cn.cjq.entity; import io.minio.messages.Item; import io.minio.messages.Owner; import lombok.AllArgsConstructor; import lombok.Data; import java.util.Date; /** * @author Kartist */ @Data @AllArgsConstructor public class MinioItem { private String objectName; private Date lastModified; private String etag; private Long size; private String storageClass; private Owner owner; private String type; public MinioItem(Item item) { this.objectName = item.objectName(); this.lastModified = item.lastModified(); this.etag = item.etag(); this.size = (long) item.size(); this.storageClass = item.storageClass(); this.owner = item.owner(); this.type = item.isDir() ? "directory" : "file"; } }View Code
package cn.cjq.entity; import io.minio.ObjectStat; import lombok.Data; import java.util.Date; @Data public class MinioObject { private String bucketName; private String name; private Date createdTime; private Long length; private String etag; private String contentType; public MinioObject(ObjectStat os) { this.bucketName = os.bucketName(); this.name = os.name(); this.createdTime = os.createdTime(); this.length = os.length(); this.etag = os.etag(); this.contentType = os.contentType(); } }View Code
7.contrller入口
package cn.cjq.controller; import cn.cjq.entity.MinioObject; import cn.cjq.util.SysFileUtils; 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.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @RestController @RequestMapping("/hello") @Slf4j public class HelloWorldController { @Autowired private SysFileUtils sysFileUtils; /** * minio檔案上傳 * * @param file * @param bucketName * @return */ @PostMapping("/upload") public MinioObject upload(@RequestPart("file") MultipartFile file, @RequestParam("bucketName") String bucketName) { final MinioObject minioObject = sysFileUtils.uploadFile(file, bucketName, true); return minioObject; } /** * minio檔案下載 * * @return */ @GetMapping("/info/**") public void getFile(HttpServletRequest request, HttpServletResponse response) { final String uri = request.getRequestURI(); String fullName = uri.replace("/hello/info", ""); sysFileUtils.downloadFile(response, fullName); } }
8.post測試minio檔案上傳
192.168.1.114:8080/hello/upload
8.瀏覽器下載圖片
url+bucketName+name:
http://192.168.1.114:8080/hello/info/cjq/2022/01/11/1106ab4078b2496699f93c0af971c737.png
參考文獻:
https://www.cnblogs.com/lvlinguang/p/15774612.html
https://www.cnblogs.com/ssgeek/p/11072053.html
https://www.jianshu.com/p/f39e7255805b