Minio+Docker搭建屬於自己的OSS儲存服務

👉 這是一個或許對你有用的社群
🐱 一對一交流/面試小冊/簡歷最佳化/求職解惑,歡迎加入芋道快速開發平臺知識星球。下面是星球提供的部分資料:
👉這是一個或許對你有用的開源專案
國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。
功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、ERPCRMAI 大模型等等功能:
  • Boot 多模組架構:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 微服務架構:https://gitee.com/zhijiantianya/yudao-cloud
  • 影片教程:https://doc.iocoder.cn
【國內首批】支援 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 雙版本 

現在 OSS 服務算是一個基礎服務了,很多雲服務廠商都有提供這樣的服務,價格也不貴。
不過對於中小公司來說,除了購買 OSS 服務之外,也可以自己搭建專業的檔案伺服器,自己搭建專門的檔案伺服器的話,曾經比較專業的做法是 FastDFS。不過 FastDFS 搭建比較麻煩,非常容易出錯,所以對各位小夥伴來說多多少少有一點門檻。
在之前的文章錄製的一些專案影片中,如果涉及到檔案上傳,基本上都是儲存在專案本地,這種方式比較省事,但是安全性不高。
所以,今天給大夥介紹一個較好的玩意 MinIO,看看這個工具帶給我們什麼驚喜。

1. MinIO 簡介

MinIO 是一個基於 Apache License v2.0 開源協議的物件儲存服務,它相容亞馬遜 S3 雲端儲存服務介面,非常適合於儲存大容量非結構化的資料,例如圖片、影片、日誌檔案、備份資料和容器/虛擬機器映象等,而一個物件檔案可以是任意大小,從幾 KB 到最大 5T 不等。
MinIO 是一個非常輕量的服務,可以很簡單的和其他應用的結合,類似 NodeJS, Redis 或者 MySQL。
簡單來說,可以使用 MinIO 來搭建一個物件儲存服務,而且 MinIO 的 Java 客戶端和亞馬遜的 S3 雲端儲存服務客戶端介面相容,換句話說,你會往 MinIO 上存資料,就會往 S3 上存資料。
MinIO 的特點:
  1. 相容 Amazon S3:可以使用 MinIO SDK,MinIO Client,AWS SDK 和 AWS CLI 訪問 MinIO 伺服器。
  2. 較強的資料保護能力:MinIO 使用 Minio Erasure Code 來防止硬體故障。
  3. 高度可用:MinIO 伺服器可以容忍分散式設定中高達(N/2)-1 節點故障。
  4. 支援 Lambda 計算。
  5. 具有加密和防篡改功能:MinIO 為加密資料提供了機密性,完整性和真實性保證,而且效能開銷微乎其微。使用 AES-256-GCM,ChaCha20-Poly1305 和 AES-CBC 支援伺服器端和客戶端加密。
  6. 可對接後端儲存:除了 MinIO 自己的檔案系統,還支援 DAS、 JBODs、NAS、Google 雲端儲存和 Azure Blob 儲存。
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 影片教程:https://doc.iocoder.cn/video/

2. MinIO 安裝

不廢話了,趕緊裝一個體驗一把吧。
為了省事,咱們就直接用 docker 來安裝吧。
我們執行如下命令,安裝 MinIO:

docker run -p 9000:9000 -p 9001:9001 -d minio/minio server /data --console-address 

":9000"

 --address 

":9001"

這個啟動命令中配置了兩個埠:console-address 是後臺管理的網頁埠;address 則是 API 通訊埠。以上面的啟動指令碼為例,專案啟動成功後,網頁上的訪問埠是 9000,如果我們透過 Java 程式碼上傳檔案,通訊埠則是 9001。
專案啟動成功後,瀏覽器位址列輸入 http://127.0.0.1:9000/login 即可訪問到 MinIO 的後端頁面:
預設的登入使用者名稱和密碼均為 minioadmin
登入成功之後,我們首先建立一個 bucket,將來我們上傳的檔案都處於 bucket 之中,如下:
建立成功之後,我們還需要設定一下桶的讀取許可權,確保檔案將來上傳成功之後可以讀取到,點選左上角的設定按鈕進行設定,如下:
設定完成後,接下來我們就可以往這個桶中上傳資源了,如下圖:
上傳完成後,就可以看到剛剛上傳的檔案了:
上傳成功後,點選檔案,然後點選右邊的 Share 按鈕會彈出來檔案的訪問連結,由於我們已經設定了檔案可讀,因此可以不用管這裡的連結有效期了,直接透過路徑的前面部分就可以訪問到剛剛上傳的圖片了,如下:
現在檔案就可上傳可訪問了。是不是比 FastDFS 容易多了!
不過前面這種安裝方式其實有點小問題,因為我們沒有為 docker 容器設定資料卷,所以如果你把 docker 容器不小心刪除了,那麼資料也就沒了!
所以我們要設定資料卷。
修正後的 docker 指令碼如下:

docker run -p 9000:9000 -p 9001:9001 -d --name minio -v /Users/sang/minio/data:/data -v /Users/sang/minio/config:/root/.minio -e 

"MINIO_ROOT_USER=javaboy"

 -e 

"MINIO_ROOT_PASSWORD=123@45678"

 minio/minio server /data --console-address 

":9000"

 --address 

":9001"

主要是加了資料卷對映功能,將 MinIO 的資料和配置檔案對映到宿主機上,這樣將來即使容器刪除了,資料也都還在。
注意上面也自定義了登入使用者名稱和密碼。
按照上面的命令,重新建立容器之後,我們也建立一個桶並上傳檔案,上傳成功之後,我們就可以在本地對應的資料夾看到我們上傳的檔案,如下:
基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/yudao-cloud
  • 影片教程:https://doc.iocoder.cn/video/

3. 整合 Spring Boot

接下來我們再來看看在 Spring Boot 中如何玩 MinIO。
首先我們建立一個 Spring Boot 專案,引入 Web 依賴。
專案建立成功之後,我們再來手動新增一下 MinIO 的依賴,如下:
<dependency>
<groupId>

io.minio

</groupId>
<artifactId>

minio

</artifactId>
<version>

8.2.1

</version>
</dependency>

接下來我們來配置一下 application.yaml,配置一下檔案上傳所需要的基本資訊:
minio:
endpoint:http://localhost:9001
accessKey:javaboy
secretKey:123@45678
nginxHost:http://local.javaboy.org:9001
這裡四個屬性:
  1. endpoint:這是 MinIO 的 API 通訊地址。
  2. accessKey 和 secretKey 是通訊的使用者名稱和密碼,這跟網頁上登入時候的使用者名稱密碼一致。
  3. nginxHost:這個配置用來生成上傳檔案的訪問路徑。對於這個路徑,有的小夥伴可能會有疑問,nginxHost 不就是 endpoint 嗎?為什麼還要單獨配置?因為對於檔案伺服器而言,我們上傳檔案是透過 MinIO,但是訪問的時候不一定透過 MinIO,我們可能會自己搭建一個 Nginx 伺服器,透過 Nginx 伺服器來訪問上傳後的資源,大家知道 Nginx 非常擅長於做這個事情,效率非常高。所以這裡的 nginxHost 其實是指 Nginx 的訪問路徑。
接下來我們提供一個 MinioProperties 來接收這裡的四個屬性,如下:
@ConfigurationProperties

(prefix = 

"minio"

)

publicclassMinioProperties

{

/**

     * 連線地址

     */


private

 String endpoint;

/**

     * 使用者名稱

     */


private

 String accessKey;

/**

     * 密碼

     */


private

 String secretKey;

/**

     * 域名

     */


private

 String nginxHost;

public String getEndpoint()

{

return

 endpoint;

    }

publicvoidsetEndpoint(String endpoint)

{

this

.endpoint = endpoint;

    }

public String getAccessKey()

{

return

 accessKey;

    }

publicvoidsetAccessKey(String accessKey)

{

this

.accessKey = accessKey;

    }

public String getSecretKey()

{

return

 secretKey;

    }

publicvoidsetSecretKey(String secretKey)

{

this

.secretKey = secretKey;

    }

public String getNginxHost()

{

return

 nginxHost;

    }

publicvoidsetNginxHost(String nginxHost)

{

this

.nginxHost = nginxHost;

    }

}

將 application.yaml 中相關的配置注入到這個配置類中來。
接下來我們需要提供一個 MinIOClient,透過這個客戶端工具可以操作 MinIO,如下:
@Configuration
@EnableConfigurationProperties

(MinioProperties

.

class

)

publicclassMinioConfig

{

@Autowired
private

 MinioProperties minioProperties;

/**

     * 獲取MinioClient

     */


@Bean
public MinioClient minioClient()

{

return

 MinioClient.builder()

                .endpoint(minioProperties.getEndpoint())

                .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())

                .build();

    }
}

這個也沒啥好說的,傳入通訊地址以及使用者名稱密碼,就可以構建出一個 MinioClient 出來。
當檔案上傳成功之後,我們可以透過 MinIO 去訪問,也可以透過 Nginx 訪問,所以接下來我們就需要提供一個類,來封裝這兩個地址:
publicclassUploadResponse

{

private

 String minIoUrl;

private

 String nginxUrl;

publicUploadResponse()

{

    }

publicUploadResponse(String minIoUrl, String nginxUrl)

{

this

.minIoUrl = minIoUrl;

this

.nginxUrl = nginxUrl;

    }

public String getMinIoUrl()

{

return

 minIoUrl;

    }

publicvoidsetMinIoUrl(String minIoUrl)

{

this

.minIoUrl = minIoUrl;

    }

public String getNginxUrl()

{

return

 nginxUrl;

    }

publicvoidsetNginxUrl(String nginxUrl)

{

this

.nginxUrl = nginxUrl;

    }

}

再來提供一個 MinIO 檔案上傳工具類:
@Component
publicclassMinioUtil

{

@Autowired
private

 MinioProperties minioProperties;

@Autowired
private

 MinioClient client;

/**

     * 建立bucket

     */


publicvoidcreateBucket(String bucketName)throws Exception 

{

if

 (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {

            client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());

        }

    }

/**

     * 上傳檔案

     */


public UploadResponse uploadFile(MultipartFile file, String bucketName)throws Exception 

{

//判斷檔案是否為空
if

 (

null

 == file || 

0

 == file.getSize()) {

returnnull

;

        }

//判斷儲存桶是否存在  不存在則建立

        createBucket(bucketName);

//檔名

        String originalFilename = file.getOriginalFilename();

//新的檔名 = 儲存桶檔名_時間戳.字尾名
assert

 originalFilename != 

null

;

        SimpleDateFormat format = 

new

 SimpleDateFormat(

"yyyy-MM-dd"

);

        String fileName = bucketName + 

"_"

 +

                System.currentTimeMillis() + 

"_"

 + format.format(

new

 Date()) + 

"_"

 + 

new

 Random().nextInt(

1000

) +

                originalFilename.substring(originalFilename.lastIndexOf(

"."

));

//開始上傳

        client.putObject(

                PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(

                        file.getInputStream(), file.getSize(), -

1

)

                        .contentType(file.getContentType())

                        .build());

        String url = minioProperties.getEndpoint() + 

"/"

 + bucketName + 

"/"

 + fileName;

        String urlHost = minioProperties.getNginxHost() + 

"/"

 + bucketName + 

"/"

 + fileName;

returnnew

 UploadResponse(url, urlHost);

    }

/**

     * 獲取全部bucket

     *

     * 

@return

     */


public List<Bucket> getAllBuckets()throws Exception 

{

return

 client.listBuckets();

    }

/**

     * 根據bucketName獲取資訊

     *

     * 

@param

 bucketName bucket名稱

     */


public Optional<Bucket> getBucket(String bucketName)throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, InternalException, ErrorResponseException, ServerException, XmlParserException, ServerException 

{

return

 client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();

    }

/**

     * 根據bucketName刪除資訊

     *

     * 

@param

 bucketName bucket名稱

     */


publicvoidremoveBucket(String bucketName)throws Exception 

{

        client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());

    }

/**

     * 獲取⽂件外鏈

     *

     * 

@param

 bucketName bucket名稱

     * 

@param

 objectName ⽂件名稱

     * 

@param

 expires    過期時間 <=7

     * 

@return

 url

     */


public String getObjectURL(String bucketName, String objectName, Integer expires)throws Exception 

{

return

 client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).expiry(expires).build());

    }

/**

     * 獲取⽂件

     *

     * 

@param

 bucketName bucket名稱

     * 

@param

 objectName ⽂件名稱

     * 

@return

 ⼆進位制流

     */


public InputStream getObject(String bucketName, String objectName)throws Exception 

{

return

 client.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());

    }

/**

     * 上傳⽂件

     *

     * 

@param

 bucketName bucket名稱

     * 

@param

 objectName ⽂件名稱

     * 

@param

 stream     ⽂件流

     * 

@throws

 Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject

     */


publicvoidputObject(String bucketName, String objectName, InputStream stream)throws

            Exception 

{

        client.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, stream.available(), -

1

).contentType(objectName.substring(objectName.lastIndexOf(

"."

))).build());

    }

/**

     * 上傳⽂件

     *

     * 

@param

 bucketName  bucket名稱

     * 

@param

 objectName  ⽂件名稱

     * 

@param

 stream      ⽂件流

     * 

@param

 size        ⼤⼩

     * 

@param

 contextType 型別

     * 

@throws

 Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject

     */


publicvoidputObject

(String bucketName, String objectName, InputStream stream, 

long

            size, String contextType)

throws Exception 

{

        client.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, size, -

1

).contentType(contextType).build());

    }

/**

     * 獲取⽂件資訊

     *

     * 

@param

 bucketName bucket名稱

     * 

@param

 objectName ⽂件名稱

     * 

@throws

 Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject

     */


public StatObjectResponse getObjectInfo(String bucketName, String objectName)throws Exception 

{

return

 client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());

    }

/**

     * 刪除⽂件

     *

     * 

@param

 bucketName bucket名稱

     * 

@param

 objectName ⽂件名稱

     * 

@throws

 Exception https://docs.minio.io/cn/java-client-apireference.html#removeObject

     */


publicvoidremoveObject(String bucketName, String objectName)throws Exception 

{

        client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());

    }

}

都是一些常規的 API 呼叫,我就不逐行解釋了,接下來我們來一個檔案上傳介面:
@RestController
publicclassFileUploadController

{

@Autowired

    MinioUtil minioUtil;

@PostMapping

(

"/upload"

)

public String fileUpload(MultipartFile file)throws Exception 

{

        UploadResponse bucket01 = minioUtil.uploadFile(file, 

"bucket01"

);

        System.out.println(

"bucket01.getMinIoUrl() = "

 + bucket01.getMinIoUrl());

        System.out.println(

"bucket01.getNginxUrl() = "

 + bucket01.getNginxUrl());

return

 bucket01.getMinIoUrl();

    }

}

好啦,大功告成。
接下來啟動 Spring Boot 專案,然後呼叫這個介面上傳檔案,上傳成功後,控制檯會列印如下資訊:
這就表示檔案上傳成功了。

4. 配置 nginx

前面提到了 MinIO 可以結合 Nginx 來使用,那我們這裡就來配一配 Nginx 看看。
為了省事,Nginx 我也選擇安裝到 docker 容器中,但是前面安裝 MinIO 時,我們已經做了資料卷對映,即上傳到 MinIO 的檔案實際上是儲存在宿主機的,所以現在也得給 Nginx 配置資料卷,將來讓 Nginx 也去 /Users/sang/minio/data 路徑下查詢檔案。
Nginx 安裝指令如下:

docker run --name nginx01 -p 8888:80 -v /Users/sang/minio/data:/usr/share/nginx/html:ro -d nginx

這裡兩個關鍵點:
  1. 設定 Nginx 埠為 8888。
  2. 將 MinIO 對映到宿主機的資料卷,再次掛載到 Nginx 上去。
大家知道,預設情況下,當我們訪問 Nginx 的時候,Nginx 給我們展示出來的資料其實就是 /usr/share/nginx/html 目錄下的,現在該目錄其實就相當於我宿主機的 /Users/sang/minio/data 目錄,所以我現在都不用修改 Nginx 的配置了,裝好之後直接使用 Nginx 即可。
好啦,接下來我們修改一下 application.yaml,如下:
minio:
endpoint:http://localhost:9001
accessKey:javaboy
secretKey:123@45678
nginxHost:http://local.javaboy.org:8888
改完之後,再次上傳檔案,此時打印出來的檔案訪問路徑如下:
現在我們透過這個 Nginx 路徑也能訪問到剛剛上傳的檔案了。

5. 小結

好啦,今天就和小夥伴們分享一下 MinIO 的用法,並結合 Nginx 搭建了一個簡單的檔案伺服器,感興趣的小夥伴可以試試哦。

歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,長按”或“掃描”下方二維碼噢
星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。
文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)

相關文章