SpringBoot + Minio 實(shí)現(xiàn)文件切片極速上傳技術(shù)
共 57959字,需瀏覽 116分鐘
·
2024-07-01 11:41
來(lái)源:juejin.cn/post/7304539173972983860
一. 概述
提示:請(qǐng)不要多個(gè)博客來(lái)回跳著看,此文章之詳細(xì)絕無(wú)僅有,融合多家之長(zhǎng),如果遇見(jiàn)報(bào)錯(cuò),請(qǐng)仔細(xì)捋一遍文章,不要忽略!我在寫的時(shí)候因?yàn)樵S多文章不全面,來(lái)回跳遇見(jiàn)許多坑,希望大家可以避免,本文章中悉數(shù)做了標(biāo)注提醒!?。?/p>
官網(wǎng)地址:
https://min.io/
文檔地址:
https://docs.min.io/
該文檔源碼地址(免費(fèi)資源):
https://download.csdn.net/download/weixin_53742691/87856930
Minio是一款開(kāi)源的對(duì)象存儲(chǔ)服務(wù)器,它可以運(yùn)行在多種操作系統(tǒng)上,包括Linux、Windows和MacOS等。它提供了一種簡(jiǎn)單、可擴(kuò)展、高可用的對(duì)象存儲(chǔ)解決方案,支持多種數(shù)據(jù)格式,包括對(duì)象、塊和文件等。
以下是Minio的主要特點(diǎn):
-
簡(jiǎn)單易用: Minio的安裝和配置非常簡(jiǎn)單,只需要下載并運(yùn)行相應(yīng)的二進(jìn)制文件即可。它提供了一個(gè)Web UI,可以通過(guò)界面管理存儲(chǔ)桶和對(duì)象。
-
可擴(kuò)展性: Minio可以輕松地?cái)U(kuò)展到多個(gè)節(jié)點(diǎn),以提供高可用性和容錯(cuò)能力。它支持多種部署模式,包括單節(jié)點(diǎn)、主從復(fù)制和集群等。
-
高可用性: Minio提供了多種機(jī)制來(lái)保證數(shù)據(jù)的可靠性和可用性,包括冗余備份、數(shù)據(jù)復(fù)制和故障轉(zhuǎn)移等。
-
安全性: Minio提供了多種安全機(jī)制來(lái)保護(hù)數(shù)據(jù)的機(jī)密性和完整性,包括SSL/TLS加密、訪問(wèn)控制和數(shù)據(jù)加密等。
-
多語(yǔ)言支持: Minio支持多種編程語(yǔ)言,包括Java、Python、Ruby和Go等。
-
社區(qū)支持: Minio是一個(gè)開(kāi)源項(xiàng)目,擁有龐大的社區(qū)支持和貢獻(xiàn)者。它的源代碼可以在GitHub上獲得,并且有一個(gè)活躍的郵件列表和論壇。
-
對(duì)象存儲(chǔ): Minio的核心功能是對(duì)象存儲(chǔ)。它允許用戶上傳和下載任意數(shù)量和大小的對(duì)象,并提供了多種API和SDK來(lái)訪問(wèn)這些對(duì)象。
-
塊存儲(chǔ): Minio還支持塊存儲(chǔ),允許用戶上傳和下載大型文件(例如圖像或視頻)。塊存儲(chǔ)是一種快速、高效的方式來(lái)處理大型文件。
-
文件存儲(chǔ): Minio還支持文件存儲(chǔ),允許用戶上傳和下載單個(gè)文件。文件存儲(chǔ)是一種簡(jiǎn)單、快速的方式來(lái)處理小型文件。
總之,Minio是一款強(qiáng)大、靈活、可擴(kuò)展的對(duì)象存儲(chǔ)服務(wù)器,適用于各種應(yīng)用場(chǎng)景,包括云存儲(chǔ)、大數(shù)據(jù)存儲(chǔ)和物聯(lián)網(wǎng)等。
二. 應(yīng)用場(chǎng)景
MinIO是一種高性能、擴(kuò)展性好的對(duì)象存儲(chǔ)系統(tǒng),它可以適用于許多應(yīng)用場(chǎng)景,其中包括但不限于以下幾種:
-
大規(guī)模數(shù)據(jù)存儲(chǔ): 由于MinIO使用分布式環(huán)境來(lái)存儲(chǔ)數(shù)據(jù),因此可以輕松擴(kuò)展以滿足需要管理大量數(shù)據(jù)的組織和企業(yè)的需求。
-
圖像和媒體存儲(chǔ): 由于MinIO對(duì)原始二進(jìn)制數(shù)據(jù)進(jìn)行了優(yōu)化,因此非常適合存儲(chǔ)圖像、音頻和視頻等媒體文件。它還支持WebP、JPEG和PNG等格式,可在多種設(shè)備和瀏覽器上工作。
-
云原生應(yīng)用程序: MinIO是一個(gè)云原生的對(duì)象存儲(chǔ)系統(tǒng),可以與Kubernetes、Docker Swarm和Mesosphere等容器編排工具無(wú)縫集成,可以很好地滿足基于云的應(yīng)用程序的需求。
-
數(shù)據(jù)保護(hù)和災(zāi)難恢復(fù): MinIO的多副本寫入功能和內(nèi)置的糾刪碼支持,使得數(shù)據(jù)備份和恢復(fù)變得簡(jiǎn)單而強(qiáng)大。
-
分布式計(jì)算和機(jī)器學(xué)習(xí): MinIO提供STS(S3 Select)和HDFS接口,支持在數(shù)據(jù)倉(cāng)庫(kù)中直接運(yùn)行SQL查詢和MapReduce等并行處理框架。這使得它成為用于Big Data、AI和ML等分布式計(jì)算任務(wù)的理想選擇。
需要注意的是,以上列出的應(yīng)用場(chǎng)景并不是MinIO所有可適用的場(chǎng)景。具體取決于每個(gè)使用情況的細(xì)節(jié)和需求。
三. Minio實(shí)現(xiàn)分片上傳的主要步驟
使用SpringBoot和MinIO實(shí)現(xiàn)分片上傳、秒傳、續(xù)傳主要包含以下幾個(gè)步驟:
-
前端選擇文件并對(duì)其進(jìn)行切割: 可以使用JavaScript等前端技術(shù)將文件切成多個(gè)片段,并為每個(gè)片段生成唯一標(biāo)識(shí)。
-
將每個(gè)分片上傳到MinIO對(duì)象存儲(chǔ): 調(diào)用MinIO的Java SDK將每個(gè)分片上傳到MinIO中,每個(gè)分片的KEY名稱包含基礎(chǔ)名稱和片段ID。
-
將所有分片合并成最終文件: 在前端完成所有分片的上傳之后,在后臺(tái)開(kāi)發(fā)一個(gè)接口,按照唯一標(biāo)識(shí)將所有分片合并成最終文件。合并過(guò)程可以在應(yīng)用服務(wù)器上完成,也可以使用MinIO Object Storage本身的合并功能完成。
-
實(shí)現(xiàn)秒傳: 在前端上傳分片之前,通過(guò)請(qǐng)求后臺(tái)接口來(lái)根據(jù)文件名稱和文件MD5值判斷該文件是否已經(jīng)存在,如果存在則可以直接返回文件URL,即可實(shí)現(xiàn)秒傳。
-
實(shí)現(xiàn)續(xù)傳: 在前端上傳分片時(shí)出現(xiàn)了網(wǎng)絡(luò)問(wèn)題或客戶端故障導(dǎo)致文件上傳被中斷,這時(shí)候只需記錄已上傳的分片序列號(hào)和狀態(tài)標(biāo)志,從下一個(gè)分片重新開(kāi)始上傳即可。
-
處理錯(cuò)誤和異常: 在文件上傳過(guò)程中可能會(huì)遇到各種問(wèn)題,比如服務(wù)故障、網(wǎng)絡(luò)中斷、客戶端處理超時(shí)等。因此需要加入錯(cuò)誤和異常處理,保證整個(gè)上傳過(guò)程順利進(jìn)行。
總體而言,使用SpringBoot和MinIO實(shí)現(xiàn)分片上傳、秒傳、續(xù)傳的難度不算大,可以根據(jù)上述步驟進(jìn)行開(kāi)發(fā)和實(shí)現(xiàn)。
四. Centos7安裝Minio
創(chuàng)建目標(biāo)文件夾
mkdir minio
使用docker查看目標(biāo)鏡像狀況
大家需要注意,此處我們首先需要安裝docker,對(duì)于相關(guān)安裝教程,大家可以查看我之前的文章,按部就班就可以,此處不再贅述!??!
docker search minio
使用docker拉去鏡像
docker pull minio/minio
查看鏡像是否拉取成功
docker images
啟動(dòng)Minio容器
docker run -p 9000:9000 -p 9090:9090 --net=host --name minio -d --restart=always -e "MINIO_ACCESS_KEY=IT@WangHui" -e "MINIO_SECRET_KEY=IT@WangHui" minio/minio server /data --console-address ":9000" -address ":9090"
注意一下,對(duì)于密碼強(qiáng)度是有要求的,不然報(bào)錯(cuò)
這是一個(gè)運(yùn)行 MinIO 容器的 Docker 命令,具體參數(shù)解釋如下:
-
-p: 映射容器內(nèi)部的端口到宿主機(jī)上。其中 9000 和 9090 分別映射到宿主機(jī)的 9000 和 9090 端口上。
-
--net=host: 將容器加入到主機(jī)網(wǎng)絡(luò)中,共享宿主機(jī)的 IP 地址。
-
--name minio: 指定容器的名稱為 minio。
-
--restart=always: 設(shè)置容器在退出后自動(dòng)重新啟動(dòng)。
-
-e: 設(shè)置環(huán)境變量。這里設(shè)置了兩個(gè)環(huán)境變量:
MINIO_ACCESS_KEY和MINIO_SECRET_KEY,值分別為IT@WangHui和IT@WangHui。 -
--mount: 將容器內(nèi)部的目錄掛載到宿主機(jī)上。這里將容器內(nèi)的
/data目錄掛載到了宿主機(jī)的/data目錄上。 -
--console-address: 指定容器的控制臺(tái)地址。這里設(shè)置為 :9000,表示可以通過(guò)宿主機(jī)上的 9000 端口訪問(wèn)容器的控制臺(tái)。
-
-address: 指定容器的網(wǎng)絡(luò)地址。這里設(shè)置為 :9090,表示可以通過(guò)宿主機(jī)上的 9090 端口訪問(wèn)容器的服務(wù)。
提示:頁(yè)面訪問(wèn)9000,代碼里面9090
提示:頁(yè)面訪問(wèn)9000,代碼里面9090
提示:頁(yè)面訪問(wèn)9000,代碼里面9090
報(bào)錯(cuò)警告
原因:
主要是因?yàn)樵趩?dòng)docker容器的時(shí)候或做docker配置的時(shí)候,還對(duì)防火墻設(shè)置重新啟動(dòng)等配置,這樣會(huì)清除docker的相關(guān)配置,導(dǎo)致在查詢防火墻規(guī)則的時(shí)候顯示不到docker的鏈。iptables -L查詢iptables鏈。
解決:
是由于firewalld重啟導(dǎo)致,而docker重啟又會(huì)將其注冊(cè)iptables鏈找回來(lái)。
然后刪除剛才啟動(dòng)失敗的容器,不然會(huì)繼續(xù)報(bào)錯(cuò)容器已存在
systemctl restart docker #重啟docker
docker ps -a #查看運(yùn)行容器
docker rm -f minio #根據(jù)容器名刪除容器(自己注意辨別自己的)
當(dāng)啟動(dòng)后在瀏覽器訪問(wèn)http://localhost:9000就可以訪問(wèn)minio的圖形化界面了,如圖所示:
用戶名密碼就是啟動(dòng)參數(shù)里面的數(shù)據(jù)
如果訪問(wèn)失敗,那就是防火墻問(wèn)題或者是啟動(dòng)參數(shù)最后兩項(xiàng)沒(méi)有添加,再不會(huì)有其他的,除非容器沒(méi)有啟動(dòng)成功
查看放行端口可以使用如下命令
firewall-cmd --list-ports
要放行CentOS 7上的9000端口和9090端口,您可以按照以下步驟操作:
1.檢查防火墻狀態(tài)
使用以下命令檢查防火墻狀態(tài):
systemctl status firewalld
如果防火墻已停止,則啟動(dòng)它:
systemctl start firewalld
2.允許9000端口通過(guò)防火墻
使用以下命令允許TCP流量通過(guò)9000端口:
firewall-cmd --zone=public --add-port=9000/tcp --permanent
firewall-cmd --zone=public --add-port=9090/tcp --permanent
這將向防火墻添加一個(gè)規(guī)則,以允許TCP流量通過(guò)9000端口。要永久保存此更改,請(qǐng)運(yùn)行以下命令:
firewall-cmd --reload
3.重新啟動(dòng)防火墻服務(wù)shell
使用以下命令重新啟動(dòng)防火墻服務(wù):
systemctl restart firewalld
現(xiàn)在,您已經(jīng)成功地放行了CentOS 7上的9000和9090端口。
五. 搭建springboot 環(huán)境
代碼結(jié)構(gòu)
引入項(xiàng)目依賴
<!-- 操作minio的java客戶端-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.2</version>
</dependency>
<!-- 操作minio的java客戶端-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
</dependency>
<!-- jwt鑒權(quán)相應(yīng)依賴-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
</dependency>
創(chuàng)建容器桶
獲取API訪問(wèn)憑證
編寫配置文件
server:
port: 8080
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
#minio配置
minio:
access-key: dAMaxkWaXUD1CV1JHbqw
secret-key: AXt3SD0JFkDENFbMeJKOOQb5wj8KvabZWu33Rs84
url: http://192.168.18.14:9090 #訪問(wèn)地址
bucket-name: wanghui
首先是服務(wù)器的配置:
-
端口號(hào)為8080,用于監(jiān)聽(tīng)請(qǐng)求。
-
使用了一個(gè)Servlet來(lái)處理
multipart/form-data類型的請(qǐng)求。 -
在接收到
multipart/form-data類型的請(qǐng)求時(shí),會(huì)將上傳的文件大小限制在10MB以內(nèi),并將請(qǐng)求大小限制在10MB以內(nèi)。
接下來(lái)是minio的配置:
-
access-key和secret-key是訪問(wèn)minio服務(wù)的憑證,需要根據(jù)實(shí)際情況進(jìn)行填寫。 -
url是minio服務(wù)的地址,需要根據(jù)實(shí)際情況進(jìn)行填寫。
-
bucket-name是存儲(chǔ)文件的桶名,需要根據(jù)實(shí)際情況進(jìn)行填寫。
http請(qǐng)求狀態(tài)
package com.xiaohui.utils;
/**
* @Description http請(qǐng)求狀態(tài)
* @Author IT小輝同學(xué)
* @Date 2023/06/01
*/
public class HttpStatus
{
/**
* 操作成功
*/
public static final int SUCCESS = 200;
/**
* 對(duì)象創(chuàng)建成功
*/
public static final int CREATED = 201;
/**
* 請(qǐng)求已經(jīng)被接受
*/
public static final int ACCEPTED = 202;
/**
* 操作已經(jīng)執(zhí)行成功,但是沒(méi)有返回?cái)?shù)據(jù)
*/
public static final int NO_CONTENT = 204;
/**
* 資源已被移除
*/
public static final int MOVED_PERM = 301;
/**
* 重定向
*/
public static final int SEE_OTHER = 303;
/**
* 資源沒(méi)有被修改
*/
public static final int NOT_MODIFIED = 304;
/**
* 參數(shù)列表錯(cuò)誤(缺少,格式不匹配)
*/
public static final int BAD_REQUEST = 400;
/**
* 未授權(quán)
*/
public static final int UNAUTHORIZED = 401;
/**
* 訪問(wèn)受限,授權(quán)過(guò)期
*/
public static final int FORBIDDEN = 403;
/**
* 資源,服務(wù)未找到
*/
public static final int NOT_FOUND = 404;
/**
* 不允許的http方法
*/
public static final int BAD_METHOD = 405;
/**
* 資源沖突,或者資源被鎖
*/
public static final int CONFLICT = 409;
/**
* 不支持的數(shù)據(jù),媒體類型
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
* 系統(tǒng)內(nèi)部錯(cuò)誤
*/
public static final int ERROR = 500;
/**
* 接口未實(shí)現(xiàn)
*/
public static final int NOT_IMPLEMENTED = 501;
/**
* 系統(tǒng)警告消息
*/
public static final int WARN = 601;
}
通用常量信息
package com.xiaohui.utils;
import io.jsonwebtoken.Claims;
/**
* @Description 通用常量信息
* @Author IT小輝同學(xué)
* @Date 2023/06/01
*/
public class Constants
{
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* GBK 字符集
*/
public static final String GBK = "GBK";
/**
* www主域
*/
public static final String WWW = "www.";
/**
* http請(qǐng)求
*/
public static final String HTTP = "http://";
/**
* https請(qǐng)求
*/
public static final String HTTPS = "https://";
/**
* 通用成功標(biāo)識(shí)
*/
public static final String SUCCESS = "0";
/**
* 通用失敗標(biāo)識(shí)
*/
public static final String FAIL = "1";
/**
* 登錄成功
*/
public static final String LOGIN_SUCCESS = "Success";
/**
* 注銷
*/
public static final String LOGOUT = "Logout";
/**
* 注冊(cè)
*/
public static final String REGISTER = "Register";
/**
* 登錄失敗
*/
public static final String LOGIN_FAIL = "Error";
/**
* 驗(yàn)證碼有效期(分鐘)
*/
public static final Integer CAPTCHA_EXPIRATION = 2;
/**
* 令牌
*/
public static final String TOKEN = "token";
/**
* 令牌前綴
*/
public static final String TOKEN_PREFIX = "Bearer ";
/**
* 令牌前綴
*/
public static final String LOGIN_USER_KEY = "login_user_key";
/**
* 用戶ID
*/
public static final String JWT_USERID = "userid";
/**
* 用戶名稱
*/
public static final String JWT_USERNAME = Claims.SUBJECT;
/**
* 用戶頭像
*/
public static final String JWT_AVATAR = "avatar";
/**
* 創(chuàng)建時(shí)間
*/
public static final String JWT_CREATED = "created";
/**
* 用戶權(quán)限
*/
public static final String JWT_AUTHORITIES = "authorities";
/**
* 資源映射路徑 前綴
*/
public static final String RESOURCE_PREFIX = "/profile";
/**
* RMI 遠(yuǎn)程方法調(diào)用
*/
public static final String LOOKUP_RMI = "rmi:";
/**
* LDAP 遠(yuǎn)程方法調(diào)用
*/
public static final String LOOKUP_LDAP = "ldap:";
/**
* LDAPS 遠(yuǎn)程方法調(diào)用
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 定時(shí)任務(wù)白名單配置(僅允許訪問(wèn)的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
/**
* 定時(shí)任務(wù)違規(guī)的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config" };
}
創(chuàng)建Minio的配置類
package com.xiaohui.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;
/**
* @Description minio配置
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.minio")
public class MinioConfig {
private String accessKey;
private String secretKey;
private String url;
private String bucketName;
@Bean
public MinioClient minioClient(){
return MinioClient.builder()
.endpoint(url)
.credentials(accessKey,secretKey)
.build();
}
}
這段代碼是Java中的一個(gè)配置類,用于配置與MinIO(一個(gè)對(duì)象存儲(chǔ)服務(wù))相關(guān)的屬性。具體來(lái)說(shuō):
-
@Configuration注解表示這是一個(gè)配置類,用于將該類中定義的屬性注入到其他組件中使用。 -
@ConfigurationProperties注解表示該類使用了spring.minio.*前綴的屬性來(lái)配置Minio相關(guān)的屬性。 -
@Data注解表示自動(dòng)生成getter和setter方法,簡(jiǎn)化了代碼編寫。 -
accessKey和secretKey屬性分別表示訪問(wèn)密鑰和密鑰值,用于連接到MinIO服務(wù)。 -
url屬性表示MinIO服務(wù)的URL地址。
-
bucketName屬性表示存儲(chǔ)桶名稱。 -
@Bean注解表示將minioClient()方法返回的對(duì)象注冊(cè)為bean,以便在其他組件中使用。 -
minioClient()方法返回了一個(gè)MinioClient對(duì)象,用于連接到MinIO服務(wù)并操作存儲(chǔ)桶。其中,endpoint()方法用于設(shè)置MinIO服務(wù)的URL地址,credentials()方法用于設(shè)置訪問(wèn)密鑰和密鑰值。
創(chuàng)建Minio的工具類
package com.xiaohui.utils;
import com.xiaohui.config.MinioConfig;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.SneakyThrows;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Description Minio工具類
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
@Component
public class MinioUtils {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioConfig configuration;
/**
* @param name 名字
* @return boolean
* @Description description: 判斷bucket是否存在,不存在則創(chuàng)建
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
public boolean existBucket(String name) {
boolean exists;
try {
exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
exists = true;
}
} catch (Exception e) {
e.printStackTrace();
exists = false;
}
return exists;
}
/**
* @param bucketName 存儲(chǔ)bucket名稱
* @return {@link Boolean }
* @Description 創(chuàng)建存儲(chǔ)bucket
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
public Boolean makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* @param bucketName 存儲(chǔ)bucket名稱
* @return {@link Boolean }
* @Description 刪除存儲(chǔ)bucket
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
public Boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* @param fileName 文件名稱
* @param time 時(shí)間
* @return {@link Map }
* @Description 獲取上傳臨時(shí)簽名
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
@SneakyThrows
public Map getPolicy(String fileName, ZonedDateTime time) {
PostPolicy postPolicy = new PostPolicy(configuration.getBucketName(), time);
postPolicy.addEqualsCondition("key", fileName);
try {
Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
HashMap<String, String> map1 = new HashMap<>();
map.forEach((k, v) -> {
map1.put(k.replaceAll("-", ""), v);
});
map1.put("host", configuration.getUrl() + "/" + configuration.getBucketName());
return map1;
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
}
return null;
}
/**
* @param objectName 對(duì)象名稱
* @param method 方法
* @param time 時(shí)間
* @param timeUnit 時(shí)間單位
* @return {@link String }
* @Description 獲取上傳文件的url
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
public String getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit) {
try {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(method)
.bucket(configuration.getBucketName())
.object(objectName)
.expiry(time, timeUnit).build());
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
}
return null;
}
/**
* @param file 文件
* @param fileName 文件名稱
* @Description 上傳文件
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
public void upload(MultipartFile file, String fileName) {
// 使用putObject上傳一個(gè)文件到存儲(chǔ)桶中。
try {
InputStream inputStream = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(configuration.getBucketName())
.object(fileName)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build());
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
}
}
/**
* @param objectName 對(duì)象名稱
* @param time 時(shí)間
* @param timeUnit 時(shí)間單位
* @return {@link String }
* @Description 根據(jù)filename獲取文件訪問(wèn)地址
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
public String getUrl(String objectName, int time, TimeUnit timeUnit) {
String url = null;
try {
url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(configuration.getBucketName())
.object(objectName)
.expiry(time, timeUnit).build());
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
}
return url;
}
/**
* @param fileName
* @return {@link ResponseEntity }<{@link byte[] }>
* @Description description: 下載文件
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
public ResponseEntity<byte[]> download(String fileName) {
ResponseEntity<byte[]> responseEntity = null;
InputStream in = null;
ByteArrayOutputStream out = null;
try {
in = minioClient.getObject(GetObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
out = new ByteArrayOutputStream();
IOUtils.copy(in, out);
//封裝返回值
byte[] bytes = out.toByteArray();
HttpHeaders headers = new HttpHeaders();
try {
headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
headers.setContentLength(bytes.length);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setAccessControlExposeHeaders(Arrays.asList("*"));
responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.SUCCESS);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseEntity;
}
/**
* @param objectFile 對(duì)象文件
* @return {@link String }
* @Description 根據(jù)文件名和桶獲取文件路徑
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
public String getFileUrl(String objectFile) {
try {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(configuration.getBucketName())
.object(objectFile)
.build()
);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
該代碼是一個(gè)工具類,用于使用阿里云的對(duì)象存儲(chǔ)服務(wù)(OSS)進(jìn)行文件上傳和下載。具體功能如下:
-
getPolicy(String fileName, ZonedDateTime time):根據(jù)文件名和時(shí)間戳獲取上傳臨時(shí)簽名。 -
getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit):獲取上傳文件的url。 -
upload(MultipartFile file, String fileName):將文件上傳到OSS中。 -
getUrl(String objectName, int time, TimeUnit timeUnit):獲取文件的下載url。
代碼中使用了枚舉類型來(lái)定義不同的上傳和下載方法。
使用了注解@Autowired來(lái)自動(dòng)注入MinioClient對(duì)象。
該工具類沒(méi)有提供異常處理機(jī)制,需要根據(jù)實(shí)際情況進(jìn)行補(bǔ)充。
創(chuàng)建Ajax請(qǐng)求工具類
/**
* @Description ajax結(jié)果
* @Author IT小輝同學(xué)
* @Date 2023/06/01
*/
public class AjaxResult extends HashMap<String, Object>
{
private static final long serialVersionUID = 1L;
/** 狀態(tài)碼 */
public static final String CODE_TAG = "code";
/** 返回內(nèi)容 */
public static final String MSG_TAG = "msg";
/** 數(shù)據(jù)對(duì)象 */
public static final String DATA_TAG = "data";
/**
* 初始化一個(gè)新創(chuàng)建的 AjaxResult 對(duì)象,使其表示一個(gè)空消息。
*/
public AjaxResult()
{
}
/**
* 初始化一個(gè)新創(chuàng)建的 AjaxResult 對(duì)象
*
* @param code 狀態(tài)碼
* @param msg 返回內(nèi)容
*/
public AjaxResult(int code, String msg)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
/**
* 初始化一個(gè)新創(chuàng)建的 AjaxResult 對(duì)象
*
* @param code 狀態(tài)碼
* @param msg 返回內(nèi)容
* @param data 數(shù)據(jù)對(duì)象
*/
public AjaxResult(int code, String msg, Object data)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if (data!=null)
{
super.put(DATA_TAG, data);
}
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success()
{
return AjaxResult.success("操作成功");
}
/**
* 返回成功數(shù)據(jù)
*
* @return 成功消息
*/
public static AjaxResult success(Object data)
{
return AjaxResult.success("操作成功", data);
}
/**
* 返回成功消息
*
* @param msg 返回內(nèi)容
* @return 成功消息
*/
public static AjaxResult success(String msg)
{
return AjaxResult.success(msg, null);
}
/**
* 返回成功消息
*
* @param msg 返回內(nèi)容
* @param data 數(shù)據(jù)對(duì)象
* @return 成功消息
*/
public static AjaxResult success(String msg, Object data)
{
return new AjaxResult(HttpStatus.SUCCESS, msg, data);
}
/**
* 返回警告消息
*
* @param msg 返回內(nèi)容
* @return 警告消息
*/
public static AjaxResult warn(String msg)
{
return AjaxResult.warn(msg, null);
}
/**
* 返回警告消息
*
* @param msg 返回內(nèi)容
* @param data 數(shù)據(jù)對(duì)象
* @return 警告消息
*/
public static AjaxResult warn(String msg, Object data)
{
return new AjaxResult(HttpStatus.WARN, msg, data);
}
/**
* 返回錯(cuò)誤消息
*
* @return 錯(cuò)誤消息
*/
public static AjaxResult error()
{
return AjaxResult.error("操作失敗");
}
/**
* 返回錯(cuò)誤消息
*
* @param msg 返回內(nèi)容
* @return 錯(cuò)誤消息
*/
public static AjaxResult error(String msg)
{
return AjaxResult.error(msg, null);
}
/**
* 返回錯(cuò)誤消息
*
* @param msg 返回內(nèi)容
* @param data 數(shù)據(jù)對(duì)象
* @return 錯(cuò)誤消息
*/
public static AjaxResult error(String msg, Object data)
{
return new AjaxResult(HttpStatus.ERROR, msg, data);
}
/**
* 返回錯(cuò)誤消息
*
* @param code 狀態(tài)碼
* @param msg 返回內(nèi)容
* @return 錯(cuò)誤消息
*/
public static AjaxResult error(int code, String msg)
{
return new AjaxResult(code, msg, null);
}
/**
* 方便鏈?zhǔn)秸{(diào)用
*
* @param key 鍵
* @param value 值
* @return 數(shù)據(jù)對(duì)象
*/
@Override
public AjaxResult put(String key, Object value)
{
super.put(key, value);
return this;
}
}
創(chuàng)建Minio文件操作接口層
package com.xiaohui.controller;
import com.xiaohui.utils.AjaxResult;
import com.xiaohui.utils.MinioUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
/**
* @Description minio文件上傳控制器
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
@CrossOrigin
@RestController
@RequestMapping("/api")
public class MinioFileUploadController {
@Autowired
private MinioUtils minioUtils;
/**
* @param file 文件
* @param fileName 文件名稱
* @return {@link AjaxResult }
* @Description 上傳文件
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
@GetMapping("/upload")
public AjaxResult uploadFile(@RequestParam("file") MultipartFile file, String fileName) {
minioUtils.upload(file, fileName);
return AjaxResult.success("上傳成功");
}
/**
* @param fileName 文件名稱
* @return {@link ResponseEntity }
* @Description dowload文件
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
@GetMapping("/dowload")
public ResponseEntity dowloadFile(@RequestParam("fileName") String fileName) {
return minioUtils.download(fileName);
}
/**
* @param fileName 文件名稱
* @return {@link AjaxResult }
* @Description 得到文件url
* @Author IT小輝同學(xué)
* @Date 2023/06/02
*/
@GetMapping("/getUrl")
public AjaxResult getFileUrl(@RequestParam("fileName") String fileName){
HashMap map=new HashMap();
map.put("FileUrl",minioUtils.getFileUrl(fileName));
return AjaxResult.success(map);
}
}
六. 功能測(cè)試
Minio大文件上傳
Minio大文件地址
程序汪接私活項(xiàng)目目錄,2023年總結(jié)
Java項(xiàng)目分享 最新整理全集,找項(xiàng)目不累啦 07版
程序汪10萬(wàn)接的無(wú)線共享充電寶項(xiàng)目,開(kāi)發(fā)周期3個(gè)月
程序汪1萬(wàn)接的企業(yè)官網(wǎng)項(xiàng)目,開(kāi)發(fā)周期15天
程序汪8萬(wàn)接的共享口罩項(xiàng)目,開(kāi)發(fā)周期1個(gè)月
程序汪8萬(wàn)塊的飲水機(jī)物聯(lián)網(wǎng)私活項(xiàng)目經(jīng)驗(yàn)分享
程序汪接的酒店在線開(kāi)房項(xiàng)目,另外一個(gè)好聽(tīng)的名字叫智慧酒店
歡迎添加程序汪個(gè)人微信 itwang008 進(jìn)粉絲群或圍觀朋友圈
