SpringBoot + Minio 實(shí)現(xiàn)文件切片極速上傳技術(shù)
共 57959字,需瀏覽 116分鐘
·
2024-07-01 11:41
來源:juejin.cn/post/7304539173972983860
一. 概述
提示:請不要多個博客來回跳著看,此文章之詳細(xì)絕無僅有,融合多家之長,如果遇見報錯,請仔細(xì)捋一遍文章,不要忽略!我在寫的時候因?yàn)樵S多文章不全面,來回跳遇見許多坑,希望大家可以避免,本文章中悉數(shù)做了標(biāo)注提醒?。?!
官網(wǎng)地址:
https://min.io/
文檔地址:
https://docs.min.io/
該文檔源碼地址(免費(fèi)資源):
https://download.csdn.net/download/weixin_53742691/87856930
Minio是一款開源的對象存儲服務(wù)器,它可以運(yùn)行在多種操作系統(tǒng)上,包括Linux、Windows和MacOS等。它提供了一種簡單、可擴(kuò)展、高可用的對象存儲解決方案,支持多種數(shù)據(jù)格式,包括對象、塊和文件等。
以下是Minio的主要特點(diǎn):
-
簡單易用: Minio的安裝和配置非常簡單,只需要下載并運(yùn)行相應(yīng)的二進(jìn)制文件即可。它提供了一個Web UI,可以通過界面管理存儲桶和對象。
-
可擴(kuò)展性: Minio可以輕松地?cái)U(kuò)展到多個節(jié)點(diǎn),以提供高可用性和容錯能力。它支持多種部署模式,包括單節(jié)點(diǎn)、主從復(fù)制和集群等。
-
高可用性: Minio提供了多種機(jī)制來保證數(shù)據(jù)的可靠性和可用性,包括冗余備份、數(shù)據(jù)復(fù)制和故障轉(zhuǎn)移等。
-
安全性: Minio提供了多種安全機(jī)制來保護(hù)數(shù)據(jù)的機(jī)密性和完整性,包括SSL/TLS加密、訪問控制和數(shù)據(jù)加密等。
-
多語言支持: Minio支持多種編程語言,包括Java、Python、Ruby和Go等。
-
社區(qū)支持: Minio是一個開源項(xiàng)目,擁有龐大的社區(qū)支持和貢獻(xiàn)者。它的源代碼可以在GitHub上獲得,并且有一個活躍的郵件列表和論壇。
-
對象存儲: Minio的核心功能是對象存儲。它允許用戶上傳和下載任意數(shù)量和大小的對象,并提供了多種API和SDK來訪問這些對象。
-
塊存儲: Minio還支持塊存儲,允許用戶上傳和下載大型文件(例如圖像或視頻)。塊存儲是一種快速、高效的方式來處理大型文件。
-
文件存儲: Minio還支持文件存儲,允許用戶上傳和下載單個文件。文件存儲是一種簡單、快速的方式來處理小型文件。
總之,Minio是一款強(qiáng)大、靈活、可擴(kuò)展的對象存儲服務(wù)器,適用于各種應(yīng)用場景,包括云存儲、大數(shù)據(jù)存儲和物聯(lián)網(wǎng)等。
二. 應(yīng)用場景
MinIO是一種高性能、擴(kuò)展性好的對象存儲系統(tǒng),它可以適用于許多應(yīng)用場景,其中包括但不限于以下幾種:
-
大規(guī)模數(shù)據(jù)存儲: 由于MinIO使用分布式環(huán)境來存儲數(shù)據(jù),因此可以輕松擴(kuò)展以滿足需要管理大量數(shù)據(jù)的組織和企業(yè)的需求。
-
圖像和媒體存儲: 由于MinIO對原始二進(jìn)制數(shù)據(jù)進(jìn)行了優(yōu)化,因此非常適合存儲圖像、音頻和視頻等媒體文件。它還支持WebP、JPEG和PNG等格式,可在多種設(shè)備和瀏覽器上工作。
-
云原生應(yīng)用程序: MinIO是一個云原生的對象存儲系統(tǒng),可以與Kubernetes、Docker Swarm和Mesosphere等容器編排工具無縫集成,可以很好地滿足基于云的應(yīng)用程序的需求。
-
數(shù)據(jù)保護(hù)和災(zāi)難恢復(fù): MinIO的多副本寫入功能和內(nèi)置的糾刪碼支持,使得數(shù)據(jù)備份和恢復(fù)變得簡單而強(qiáng)大。
-
分布式計(jì)算和機(jī)器學(xué)習(xí): MinIO提供STS(S3 Select)和HDFS接口,支持在數(shù)據(jù)倉庫中直接運(yùn)行SQL查詢和MapReduce等并行處理框架。這使得它成為用于Big Data、AI和ML等分布式計(jì)算任務(wù)的理想選擇。
需要注意的是,以上列出的應(yīng)用場景并不是MinIO所有可適用的場景。具體取決于每個使用情況的細(xì)節(jié)和需求。
三. Minio實(shí)現(xiàn)分片上傳的主要步驟
使用SpringBoot和MinIO實(shí)現(xiàn)分片上傳、秒傳、續(xù)傳主要包含以下幾個步驟:
-
前端選擇文件并對其進(jìn)行切割: 可以使用JavaScript等前端技術(shù)將文件切成多個片段,并為每個片段生成唯一標(biāo)識。
-
將每個分片上傳到MinIO對象存儲: 調(diào)用MinIO的Java SDK將每個分片上傳到MinIO中,每個分片的KEY名稱包含基礎(chǔ)名稱和片段ID。
-
將所有分片合并成最終文件: 在前端完成所有分片的上傳之后,在后臺開發(fā)一個接口,按照唯一標(biāo)識將所有分片合并成最終文件。合并過程可以在應(yīng)用服務(wù)器上完成,也可以使用MinIO Object Storage本身的合并功能完成。
-
實(shí)現(xiàn)秒傳: 在前端上傳分片之前,通過請求后臺接口來根據(jù)文件名稱和文件MD5值判斷該文件是否已經(jīng)存在,如果存在則可以直接返回文件URL,即可實(shí)現(xiàn)秒傳。
-
實(shí)現(xiàn)續(xù)傳: 在前端上傳分片時出現(xiàn)了網(wǎng)絡(luò)問題或客戶端故障導(dǎo)致文件上傳被中斷,這時候只需記錄已上傳的分片序列號和狀態(tài)標(biāo)志,從下一個分片重新開始上傳即可。
-
處理錯誤和異常: 在文件上傳過程中可能會遇到各種問題,比如服務(wù)故障、網(wǎng)絡(luò)中斷、客戶端處理超時等。因此需要加入錯誤和異常處理,保證整個上傳過程順利進(jìn)行。
總體而言,使用SpringBoot和MinIO實(shí)現(xiàn)分片上傳、秒傳、續(xù)傳的難度不算大,可以根據(jù)上述步驟進(jìn)行開發(fā)和實(shí)現(xiàn)。
四. Centos7安裝Minio
創(chuàng)建目標(biāo)文件夾
mkdir minio
使用docker查看目標(biāo)鏡像狀況
大家需要注意,此處我們首先需要安裝docker,對于相關(guān)安裝教程,大家可以查看我之前的文章,按部就班就可以,此處不再贅述!??!
docker search minio
使用docker拉去鏡像
docker pull minio/minio
查看鏡像是否拉取成功
docker images
啟動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"
注意一下,對于密碼強(qiáng)度是有要求的,不然報錯
這是一個運(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è)置容器在退出后自動重新啟動。
-
-e: 設(shè)置環(huán)境變量。這里設(shè)置了兩個環(huán)境變量:
MINIO_ACCESS_KEY和MINIO_SECRET_KEY,值分別為IT@WangHui和IT@WangHui。 -
--mount: 將容器內(nèi)部的目錄掛載到宿主機(jī)上。這里將容器內(nèi)的
/data目錄掛載到了宿主機(jī)的/data目錄上。 -
--console-address: 指定容器的控制臺地址。這里設(shè)置為 :9000,表示可以通過宿主機(jī)上的 9000 端口訪問容器的控制臺。
-
-address: 指定容器的網(wǎng)絡(luò)地址。這里設(shè)置為 :9090,表示可以通過宿主機(jī)上的 9090 端口訪問容器的服務(wù)。
提示:頁面訪問9000,代碼里面9090
提示:頁面訪問9000,代碼里面9090
提示:頁面訪問9000,代碼里面9090
報錯警告
原因:
主要是因?yàn)樵趩觗ocker容器的時候或做docker配置的時候,還對防火墻設(shè)置重新啟動等配置,這樣會清除docker的相關(guān)配置,導(dǎo)致在查詢防火墻規(guī)則的時候顯示不到docker的鏈。iptables -L查詢iptables鏈。
解決:
是由于firewalld重啟導(dǎo)致,而docker重啟又會將其注冊iptables鏈找回來。
然后刪除剛才啟動失敗的容器,不然會繼續(xù)報錯容器已存在
systemctl restart docker #重啟docker
docker ps -a #查看運(yùn)行容器
docker rm -f minio #根據(jù)容器名刪除容器(自己注意辨別自己的)
當(dāng)啟動后在瀏覽器訪問http://localhost:9000就可以訪問minio的圖形化界面了,如圖所示:
用戶名密碼就是啟動參數(shù)里面的數(shù)據(jù)
如果訪問失敗,那就是防火墻問題或者是啟動參數(shù)最后兩項(xiàng)沒有添加,再不會有其他的,除非容器沒有啟動成功
查看放行端口可以使用如下命令
firewall-cmd --list-ports
要放行CentOS 7上的9000端口和9090端口,您可以按照以下步驟操作:
1.檢查防火墻狀態(tài)
使用以下命令檢查防火墻狀態(tài):
systemctl status firewalld
如果防火墻已停止,則啟動它:
systemctl start firewalld
2.允許9000端口通過防火墻
使用以下命令允許TCP流量通過9000端口:
firewall-cmd --zone=public --add-port=9000/tcp --permanent
firewall-cmd --zone=public --add-port=9090/tcp --permanent
這將向防火墻添加一個規(guī)則,以允許TCP流量通過9000端口。要永久保存此更改,請運(yùn)行以下命令:
firewall-cmd --reload
3.重新啟動防火墻服務(wù)shell
使用以下命令重新啟動防火墻服務(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訪問憑證
編寫配置文件
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 #訪問地址
bucket-name: wanghui
首先是服務(wù)器的配置:
-
端口號為8080,用于監(jiān)聽請求。
-
使用了一個Servlet來處理
multipart/form-data類型的請求。 -
在接收到
multipart/form-data類型的請求時,會將上傳的文件大小限制在10MB以內(nèi),并將請求大小限制在10MB以內(nèi)。
接下來是minio的配置:
-
access-key和secret-key是訪問minio服務(wù)的憑證,需要根據(jù)實(shí)際情況進(jìn)行填寫。 -
url是minio服務(wù)的地址,需要根據(jù)實(shí)際情況進(jìn)行填寫。
-
bucket-name是存儲文件的桶名,需要根據(jù)實(shí)際情況進(jìn)行填寫。
http請求狀態(tài)
package com.xiaohui.utils;
/**
* @Description http請求狀態(tài)
* @Author IT小輝同學(xué)
* @Date 2023/06/01
*/
public class HttpStatus
{
/**
* 操作成功
*/
public static final int SUCCESS = 200;
/**
* 對象創(chuàng)建成功
*/
public static final int CREATED = 201;
/**
* 請求已經(jīng)被接受
*/
public static final int ACCEPTED = 202;
/**
* 操作已經(jīng)執(zhí)行成功,但是沒有返回?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;
/**
* 資源沒有被修改
*/
public static final int NOT_MODIFIED = 304;
/**
* 參數(shù)列表錯誤(缺少,格式不匹配)
*/
public static final int BAD_REQUEST = 400;
/**
* 未授權(quán)
*/
public static final int UNAUTHORIZED = 401;
/**
* 訪問受限,授權(quán)過期
*/
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)部錯誤
*/
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請求
*/
public static final String HTTP = "http://";
/**
* https請求
*/
public static final String HTTPS = "https://";
/**
* 通用成功標(biāo)識
*/
public static final String SUCCESS = "0";
/**
* 通用失敗標(biāo)識
*/
public static final String FAIL = "1";
/**
* 登錄成功
*/
public static final String LOGIN_SUCCESS = "Success";
/**
* 注銷
*/
public static final String LOGOUT = "Logout";
/**
* 注冊
*/
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)建時間
*/
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:";
/**
* 定時任務(wù)白名單配置(僅允許訪問的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
/**
* 定時任務(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中的一個配置類,用于配置與MinIO(一個對象存儲服務(wù))相關(guān)的屬性。具體來說:
-
@Configuration注解表示這是一個配置類,用于將該類中定義的屬性注入到其他組件中使用。 -
@ConfigurationProperties注解表示該類使用了spring.minio.*前綴的屬性來配置Minio相關(guān)的屬性。 -
@Data注解表示自動生成getter和setter方法,簡化了代碼編寫。 -
accessKey和secretKey屬性分別表示訪問密鑰和密鑰值,用于連接到MinIO服務(wù)。 -
url屬性表示MinIO服務(wù)的URL地址。
-
bucketName屬性表示存儲桶名稱。 -
@Bean注解表示將minioClient()方法返回的對象注冊為bean,以便在其他組件中使用。 -
minioClient()方法返回了一個MinioClient對象,用于連接到MinIO服務(wù)并操作存儲桶。其中,endpoint()方法用于設(shè)置MinIO服務(wù)的URL地址,credentials()方法用于設(shè)置訪問密鑰和密鑰值。
創(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 存儲bucket名稱
* @return {@link Boolean }
* @Description 創(chuàng)建存儲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 存儲bucket名稱
* @return {@link Boolean }
* @Description 刪除存儲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 時間
* @return {@link Map }
* @Description 獲取上傳臨時簽名
* @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 對象名稱
* @param method 方法
* @param time 時間
* @param timeUnit 時間單位
* @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上傳一個文件到存儲桶中。
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 對象名稱
* @param time 時間
* @param timeUnit 時間單位
* @return {@link String }
* @Description 根據(jù)filename獲取文件訪問地址
* @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 對象文件
* @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;
}
}
該代碼是一個工具類,用于使用阿里云的對象存儲服務(wù)(OSS)進(jìn)行文件上傳和下載。具體功能如下:
-
getPolicy(String fileName, ZonedDateTime time):根據(jù)文件名和時間戳獲取上傳臨時簽名。 -
getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit):獲取上傳文件的url。 -
upload(MultipartFile file, String fileName):將文件上傳到OSS中。 -
getUrl(String objectName, int time, TimeUnit timeUnit):獲取文件的下載url。
代碼中使用了枚舉類型來定義不同的上傳和下載方法。
使用了注解@Autowired來自動注入MinioClient對象。
該工具類沒有提供異常處理機(jī)制,需要根據(jù)實(shí)際情況進(jìn)行補(bǔ)充。
創(chuàng)建Ajax請求工具類
/**
* @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ù)對象 */
public static final String DATA_TAG = "data";
/**
* 初始化一個新創(chuàng)建的 AjaxResult 對象,使其表示一個空消息。
*/
public AjaxResult()
{
}
/**
* 初始化一個新創(chuàng)建的 AjaxResult 對象
*
* @param code 狀態(tài)碼
* @param msg 返回內(nèi)容
*/
public AjaxResult(int code, String msg)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
/**
* 初始化一個新創(chuàng)建的 AjaxResult 對象
*
* @param code 狀態(tài)碼
* @param msg 返回內(nèi)容
* @param data 數(shù)據(jù)對象
*/
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ù)對象
* @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ù)對象
* @return 警告消息
*/
public static AjaxResult warn(String msg, Object data)
{
return new AjaxResult(HttpStatus.WARN, msg, data);
}
/**
* 返回錯誤消息
*
* @return 錯誤消息
*/
public static AjaxResult error()
{
return AjaxResult.error("操作失敗");
}
/**
* 返回錯誤消息
*
* @param msg 返回內(nèi)容
* @return 錯誤消息
*/
public static AjaxResult error(String msg)
{
return AjaxResult.error(msg, null);
}
/**
* 返回錯誤消息
*
* @param msg 返回內(nèi)容
* @param data 數(shù)據(jù)對象
* @return 錯誤消息
*/
public static AjaxResult error(String msg, Object data)
{
return new AjaxResult(HttpStatus.ERROR, msg, data);
}
/**
* 返回錯誤消息
*
* @param code 狀態(tài)碼
* @param msg 返回內(nèi)容
* @return 錯誤消息
*/
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ù)對象
*/
@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);
}
}
六. 功能測試
Minio大文件上傳
Minio大文件地址
程序汪接私活項(xiàng)目目錄,2023年總結(jié)
Java項(xiàng)目分享 最新整理全集,找項(xiàng)目不累啦 07版
程序汪10萬接的無線共享充電寶項(xiàng)目,開發(fā)周期3個月
程序汪1萬接的企業(yè)官網(wǎng)項(xiàng)目,開發(fā)周期15天
程序汪8萬接的共享口罩項(xiàng)目,開發(fā)周期1個月
程序汪8萬塊的飲水機(jī)物聯(lián)網(wǎng)私活項(xiàng)目經(jīng)驗(yàn)分享
程序汪接的酒店在線開房項(xiàng)目,另外一個好聽的名字叫智慧酒店
歡迎添加程序汪個人微信 itwang008 進(jìn)粉絲群或圍觀朋友圈
