spring boot 實(shí)現(xiàn)Minio分片上傳的步驟
應(yīng)用場景
分片上傳,就是將所要上傳的文件,按照一定的大小,將整個文件分隔成多個數(shù)據(jù)塊(我們稱之為Part)來進(jìn)行分別上傳,上傳完之后再由服務(wù)端對所有上傳的文件進(jìn)行匯總整合成原始的文件。
分片上傳的場景
- 大文件上傳
- 網(wǎng)絡(luò)環(huán)境環(huán)境不好,存在需要重傳風(fēng)險的場景
分片上傳的步驟


檢查文件的代碼
在文件第一次上傳時,上傳文件的md5值,從而判斷文件是否存在minio中
public Result<Boolean> checkFile(String fileMd5) {
//正常做業(yè)務(wù)時應(yīng)該先從數(shù)據(jù)庫中查詢
//如果數(shù)據(jù)庫存在再查詢 minio
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket(bucketName)
// todo 這里固定了文件的后綴,實(shí)際情況下應(yīng)該從數(shù)據(jù)庫開始查詢,得到文件的路徑
.object(getFilePathByMd5(fileMd5,"png"))
.build();
//查詢遠(yuǎn)程服務(wù)獲取到一個流對象
try {
FilterInputStream inputStream = minioClient.getObject(getObjectArgs);
if(inputStream!=null){
//文件已存在
return Result.success(true);
}
} catch (Exception e) {
e.printStackTrace();
}
//文件不存在
return Result.success(false);
}檢查分塊的代碼
檢查分塊是前端把需要上傳的文件經(jīng)過大小計(jì)算后,算出分塊的數(shù)量,然后把循環(huán)發(fā)送文件的md5值和分塊序號,然后在minio中檢查對應(yīng)文件夾下是否有對應(yīng)的分塊,如果檢查到某一處沒有對應(yīng)的分塊,便知道傳輸中斷的位置。
public Result<Boolean> checkChunk(String fileMd5, int chunkIndex) {
//根據(jù)md5得到分塊文件所在目錄的路徑
String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
//如果數(shù)據(jù)庫存在再查詢 minio
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket(bucketName)
.object(chunkFileFolderPath+chunkIndex)
.build();
//查詢遠(yuǎn)程服務(wù)獲取到一個流對象
try {
FilterInputStream inputStream = minioClient.getObject(getObjectArgs);
if(inputStream!=null){
//文件已存在
return Result.success(true);
}
} catch (Exception e) {
e.printStackTrace();
}
//文件不存在
return Result.success(false);
}上傳分塊的代碼
public Result uploadChunk(String fileMd5, int chunk, String localChunkFilePath) {
//分塊文件的路徑
String chunkFilePath = getChunkFileFolderPath(fileMd5) + chunk;
//獲取mimeType
String mimeType = localChunkFilePath.substring(localChunkFilePath.lastIndexOf("."));
//將分塊文件上傳到minio
boolean b = addMediaFilesToMinIO(localChunkFilePath, mimeType, bucketName, chunkFilePath);
if(!b){
return Result.error("上傳分塊文件失敗");
}
//上傳成功
return Result.success(true);
}
合并分塊的代碼
合并分塊文件之前,需要檢查文件是否和源文件相同,我們通過把分塊合并后取文件的md5值和傳輸過來的MD5值作比較,如果相同則證明傳輸正確,把合并后的文件存入minio中,并清除分塊文件
public Result mergechunks(String fileMd5, int chunkTotal) {
//分塊文件所在目錄
String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
//找到所有的分塊文件
List<ComposeSource> sources = Stream.iterate(0, i -> ++i)
.limit(chunkTotal).map(i -> ComposeSource.builder()
.bucket(bucketName)
.object(chunkFileFolderPath + i).build()).collect(Collectors.toList());
//合并后文件的objectname
String objectName = getFilePathByMd5(fileMd5, "png");
//指定合并后的objectName等信息
ComposeObjectArgs composeObjectArgs = ComposeObjectArgs.builder()
.bucket(bucketName)
.object(objectName)//合并后的文件的objectname
.sources(sources)//指定源文件
.build();
//===========合并文件============
//報(bào)錯size 1048576 must be greater than 5242880,minio默認(rèn)的分塊文件大小為5M
try {
minioClient.composeObject(composeObjectArgs);
} catch (Exception e) {
e.printStackTrace();
log.error("合并文件出錯,bucket:{},objectName:{},錯誤信息:{}",bucketName,objectName,e.getMessage());
return Result.error("合并文件異常");
}
//===========校驗(yàn)合并后的和源文件是否一致,視頻上傳才成功===========
//先下載合并后的文件
File file = downloadFileFromMinIO(bucketName, objectName);
try(FileInputStream fileInputStream = new FileInputStream(file)){
//計(jì)算合并后文件的md5
String mergeFile_md5 = DigestUtils.md5Hex(fileInputStream);
//比較原始md5和合并后文件的md5
if(!fileMd5.equals(mergeFile_md5)){
log.error("校驗(yàn)合并文件md5值不一致,原始文件:{},合并文件:{}",fileMd5,mergeFile_md5);
return Result.error("文件校驗(yàn)失敗");
}
}catch (Exception e) {
return Result.error("文件校驗(yàn)失敗");
}
//==============將文件信息入庫============
// 在做業(yè)務(wù)時要將得到的路徑存入數(shù)據(jù)庫
//==========清理分塊文件=========
clearChunkFiles(chunkFileFolderPath,chunkTotal);
return Result.success(true);
}
/**
* 清除分塊文件
* @param chunkFileFolderPath 分塊文件路徑
* @param chunkTotal 分塊文件總數(shù)
*/
private void clearChunkFiles(String chunkFileFolderPath,int chunkTotal){
Iterable<DeleteObject> objects = Stream.iterate(0, i -> ++i).limit(chunkTotal).map(i -> new DeleteObject(chunkFileFolderPath+ i)).collect(Collectors.toList());;
RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build();
Iterable<io.minio.Result<DeleteError>> results = minioClient.removeObjects(removeObjectsArgs);
//要想真正刪除
results.forEach(f->{
try {
DeleteError deleteError = f.get();
} catch (Exception e) {
e.printStackTrace();
}
});
}
到此這篇關(guān)于spring boot 實(shí)現(xiàn)Minio分片上傳的文章就介紹到這了,更多相關(guān)spring boot Minio分片上傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringSecurity中PasswordEncoder的使用
密碼存儲和加密是非常重要的,本文主要介紹了SpringSecurity中PasswordEncoder的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01
JAVA實(shí)現(xiàn)基于皮爾遜相關(guān)系數(shù)的相似度詳解
這篇文章主要介紹了JAVA實(shí)現(xiàn)基于皮爾遜相關(guān)系數(shù)的相似度詳解,具有一定參考價值,需要的朋友可以了解下。2017-11-11
Java用BigDecimal類解決Double類型精度丟失的問題
這篇文章主要介紹了Java用BigDecimal類解決Double類型精度丟失的問題,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12
Spring的IOC解決程序耦合的實(shí)現(xiàn)
本文主要介紹了Spring的IOC解決程序耦合的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07
Spring?Boot攔截器和監(jiān)聽器實(shí)現(xiàn)對請求和響應(yīng)處理實(shí)戰(zhàn)
這篇文章主要介紹了Spring?Boot攔截器和監(jiān)聽器實(shí)現(xiàn)對請求和響應(yīng)處理實(shí)戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
java加載properties文件的六種方法總結(jié)
這篇文章主要介紹了java加載properties文件的六種方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-05-05
Java Class 解析器實(shí)現(xiàn)方法示例
這篇文章主要通過對class文件的分析,介紹了Java Class 解析器實(shí)現(xiàn)方法示例,具有一定參考價值,需要的朋友可以了解下。2017-09-09

