微服务架构 | 怎样解决超大附件分片上传?
導讀:分片上傳、斷點續傳,這兩個名詞對于做過或者熟悉文件上傳的朋友來說應該不會陌生,總結本篇文章希望對從事相關工作的同學能夠有所幫助或者啟發。?? ??
當我們的文件特別大的時候,上傳是不是需要很長的時間啊,這么長時間的長連接,如果網絡波動了呢?中間網絡斷開了呢?在這么長時間的過程中如果出現不穩定的情況,本次上傳的所有內容就全部失敗了,又要重新上傳。
分片上傳,就是將所要上傳的文件,按照一定的大小,將整個文件分隔成多個數據塊(我們稱之為 Part)來進行分別上傳,上傳完之后再由服務端對所有上傳的文件進行匯總整合成原始的文件。分片上傳不僅可以避免因網絡環境不好導致的一直需要從文件起始位置還是上傳的問題,還能使用多線程對不同分塊數據進行并發發送,提高發送效率,降低發送時間。
一、背景
在系統用戶量突增以后,為了更好適配各群體的定制化需求。業務上慢慢實現了支持 C 端用戶自定義布局和配置,導致配置數據讀取 IO 激增。
為了更好優化此類場景,將用戶自定義配置靜態化管理!也就是將對應的配置文件生成靜態文件,在生成靜態文件的過程中遇到棘手的問題,配置文件文件過大導致在文件上傳服務器等待時間過長,致使整個業務場景性能整體下滑。
二、生成配置文件
生成文件三大要素
-
文件名
-
文件內容
-
文件存儲格式
文件內容、文件存儲格式都好理解和處理,當然先前整理過微服務中常用的加密方式
-
微服務架構 | 微服務有哪些常用的加密方式 (一)
-
微服務架構 | 數據加密有哪些常用的加密方式(二)
這里做下補充說明,如果要想對文件內容進行加密可以考慮。但是本文的案例場景對于配置信息保密程度較低,這里不做拓展。
而對于文件名的命名規范具體結合業務場景來定,通常都是以文件概要+時間戳格式為主。但是這類命名規范容易導致文件名沖突,造成沒有必要的后續麻煩。
所以我這里對于文件名的命名做了特殊處理,有處理過前端 Route 路由經驗的應該能聯想到,文件名可以通過基于內容生成 Hash 值來代替。
在Spring 3.0?之后提供了計算摘要的的方法。
DigestUtils#md復制代碼
返回給定字節的 MD5 摘要的十六進制字符串表示形式。
md5DigestAsHex 源碼
/***?計算摘要的字節*?@param??一個十六進制摘要字符*?@return?串返回給定字節的 MD5 摘要的十六進制字符串表示形式。*/ public?static?String?md5DigestAsHex(byte[]?bytes)?{return?digestAsHexString(MD5_ALGORITHM_NAME,?bytes); }文件名、內容、后綴(存儲格式)確定后直接生成文件
/***?直接根據內容生成?文件*/ public?static?void?generateFile(String?destDirPath,?String?fileName,?String?content)?throws?FileZipException?{File?targetFile?=?new?File(destDirPath?+?File.separator?+?fileName);//確保父級目錄存在if?(!targetFile.getParentFile().exists())?{if?(!targetFile.getParentFile().mkdirs())?{throw?new?FileZipException("?path?is?not?found?");}}//設置文件編碼格式try?(PrintWriter?writer?=?new?PrintWriter(new?BufferedWriter(new?OutputStreamWriter(new?FileOutputStream(targetFile),?ENCODING))))?{writer.write(content);return;}?catch?(Exception?e)?{throw?new?FileZipException("create?file?error",e);} }通過內容生成文件優點不言而喻,可以極大減少我們主動基于內容比較來生成新的文件、如果文件內容較大生成對應的文件名相同則表示內容未做任何調整,此時我們也就不用做后續的文件更新操作。
三、分片上傳附件
所謂的分片上傳,就是將所要上傳的文件,按照一定的大小,將整個文件分隔成多個數據塊(我們稱之為 Part)來進行分別上傳,上傳完之后再由服務端對所有上傳的文件進行匯總整合成原始的文件。分片上傳不僅可以避免因網絡環境不好導致的一直需要從文件起始位置還是上傳的問題,還能使用多線程對不同分塊數據進行并發發送,提高發送效率,降低發送時間。
分片上傳主要適用于以下幾種場景:
-
網絡環境不好:當出現上傳失敗的時候,可以對失敗的 Part 進行獨立的重試,而不需要重新上傳其他的 Part。
-
斷點續傳:中途暫停之后,可以從上次上傳完成的 Part 的位置繼續上傳。
-
加速上傳:要上傳到 OSS 的本地文件很大的時候,可以并行上傳多個 Part 以加快上傳。
-
流式上傳:可以在需要上傳的文件大小還不確定的情況下開始上傳。這種場景在視頻監控等行業應用中比較常見。
-
文件較大:一般文件比較大時,默認情況下一般都會采用分片上傳。
分片上傳的整個流程大致如下:
-
將需要上傳的文件按照一定的分割規則,分割成相同大小的數據塊;
-
初始化一個分片上傳任務,返回本次分片上傳唯一標識;
-
按照一定的策略(串行或并行)發送各個分片數據塊;
-
發送完成后,服務端根據判斷數據上傳是否完整,如果完整,則進行數據塊合成得到原始文件
??定義分片規則大小
默認情況都以文件達到 20MB 進行強制分片
/***?強制分片文件大小(20MB)*/ long?FORCE_SLICE_FILE_SIZE?=?20L*?1024?*?1024;為了方便調試,強制分片文件的閾值調整為 1KB
??定義分片上傳對象
如上圖紅色序號的文件碎片,定義分片上傳對象基礎屬性包含附件文件名、原始文件大小、原始文件 MD5 值、分片總數、每個分片大小、當前分片大小、當前分片序號等
定義基礎屬于便于后續對文件合理分割、分片的合并等業務拓展,當然根據業務場景可以定義拓展屬性。
分片總數
long?totalSlices?=?fileSize?%?forceSliceSize?==?0???fileSize?/?forceSliceSize?:?fileSize?/?forceSliceSize?+?1;每個分片大小
long?eachSize?=?fileSize?%?totalSlices?==?0???fileSize?/?totalSlices?:?fileSize?/?totalSlices?+?1;原始文件的 MD5 值
MD5Util.hex(file)復制代碼
如:
當前附件大小為:3382KB,強制分片大小限制為 1024KB
通過上述計算:分片數量為 4 個,每個分片大小為 846KB
? 讀取每個分片的數據字節
標記當前字節下標,循環讀取 4 個分片的數據字節
try?(InputStream?inputStream?=?new?FileInputStream(uploadVO.getFile()))?{for?(int?i?=?0;?i?<?sliceBytesVO.getFdTotalSlices();?i++)?{//?讀取每個分片的數據字節this.readSliceBytes(i,?inputStream,?sliceBytesVO);//?調用分片上傳API的函數String?result?=?sliceApiCallFunction.apply(sliceBytesVO);if?(StringUtils.isEmpty(result))?{continue;}return?result;} }?catch?(IOException?e)?{throw?e; }三、總結
所謂的分片上傳,就是將所要上傳的文件,按照一定的大小,將整個文件分隔成多個數據塊(我們稱之為 Part)來進行分別上傳。
處理大文件進行分片主要核心確定三大點
-
文件分片粒度大小
-
分片如何讀取
-
分片如何存儲
本篇文章主要分析和處理大文件上傳過程中如何針對大文件文件文件內容比較、進行分片處理。合理設置分片閾值以及如何讀取和標記分片。希望對從事相關工作的同學能夠有所幫助或者啟發。后續會對分片如何存儲、標記、合并文件進行詳細解讀。
原文:微服務架構 | 怎樣解決超大附件分片上傳?
總結
以上是生活随笔為你收集整理的微服务架构 | 怎样解决超大附件分片上传?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html 表格自动计算,表格怎么自动计算
- 下一篇: docker centos + pb