2021
02/28
08:36
文章作者:

JAVA實現(xiàn)大文件多線程下載

《面試篇》Http協(xié)議 中,我們有提到大文件下載和斷點續(xù)傳,本篇我們就來開發(fā)一個多線程文件下載器,最后我們用這個多線程下載器來突破下載的限速。

兄弟們看到這個標(biāo)題可能會覺得是個標(biāo)題黨,為了解決疑慮,我們先來看下最終的測試結(jié)果:

測試云盤下載的文件 46M,自己本地最大下載速度 2M

1. 單線程下載,總耗時: 603s

圖片

2. 多線程下載,50個線程,總耗時:13s

圖片

測試結(jié)果,「提速46倍」,我還是太謙虛了,只說提速30倍,此處我們覺得應(yīng)該有掌聲(我聽不到,還是點贊實在)

 

?

源碼地址:https://gitee.com/silently9527/fast-download

喜歡請記得star哦

?

HTTP協(xié)議Range請求頭

Range主要是針對只需要獲取部分資源的范圍請求,通過指定Range即可告知服務(wù)器資源的指定范圍。格式: Range: bytes=start-end

比如:獲取字節(jié)范圍 5001-10000

Range: bytes=5001-10000

也可以指定開始位置不指定結(jié)束位置,表示獲取開始位置之后的全部數(shù)據(jù)

Range: bytes=5001-

服務(wù)器接收到帶有Range的請求,會在處理請求之后返回狀態(tài)碼為206 Partial Content的響應(yīng)。

基于Range的特性,我們就可以實現(xiàn)文件的多線程下載,文件的斷點續(xù)傳

準(zhǔn)備工作

本文我們使用的SpringMVC中的RestTemplate;由于云盤的鏈接是Https,所以我們需要設(shè)置RestTemplate繞過證書驗證

  1. pom.xml

圖片pom.xml

  1. 編寫RestTemplate的構(gòu)造器,以及繞過https的證書驗證

圖片RestTemplateBuilder

  1. 在下載的過程中,我們需要知道當(dāng)前下載的速度是多少,所以需要定義一個顯示下載速度的接口

圖片DisplayDownloadSpeed

因為計算下載速度,我們需要知道每秒傳輸?shù)淖止?jié)數(shù)是多少,為了監(jiān)控傳輸數(shù)據(jù)的過程,我們需要了解SpringMVC中的接口ResponseExtractor

圖片ResponseExtractor

該接口只有一個方法,當(dāng)客戶端和服務(wù)器端連接建立之后,會調(diào)用這個方法,我們可以在這個方法中監(jiān)控下載的速度。

  1. DisplayDownloadSpeed接口的抽象實現(xiàn) AbstractDisplayDownloadSpeedResponseExtractor

圖片AbstractDisplayDownloadSpeedResponseExtractor

  1. 整個項目主要涉及到的類圖

圖片

簡單的文件下載器

這里使用的是restTemplate調(diào)用execute, 先文件獲取到字節(jié)數(shù)組, 再將字節(jié)數(shù)組直接寫到目標(biāo)文件。

這里我們需要注意的點是: 這種方式會將文件的字節(jié)數(shù)組全部放入內(nèi)存中, 及其消耗資源;我們來看看如何實現(xiàn)。

  1. 創(chuàng)建ByteArrayResponseExtractor類繼承AbstractDisplayDownloadSpeedResponseExtractor

圖片ByteArrayResponseExtractor

  1. 調(diào)用restTemplate.execute執(zhí)行下載,保存字節(jié)數(shù)據(jù)到文件中

圖片

  1. 測試下載819M的idea

圖片圖片

執(zhí)行一段時間之后,我們可以看到內(nèi)存已經(jīng)使用了800M左右,所以這種方式只能使用于小文件的下載,如果我們下載幾G的大文件,內(nèi)存肯定是不夠用的。至于下載時間,因為文件太大也沒有等下載完成就結(jié)束了程序。

單線程大文件下載

上面的方式只能下載小的文件,那大文件的下載我們該用什么方式呢?我們可以把流輸出到文件而不是內(nèi)存中。接下來我們來實現(xiàn)我們大文件的下載。

  1. 創(chuàng)建FileResponseExtractor類繼承AbstractDisplayDownloadSpeedResponseExtractor,把流輸出到文件中

圖片

  1. 文件下載器,先把流輸出到臨時下載文件(xxxxx.download),下載完成后在重命名文件

圖片

  1. 測試下載819M的idea

圖片

執(zhí)行一段時間之后,我們再看看下內(nèi)存的使用情況,發(fā)現(xiàn)這種方式內(nèi)存消耗較少,效果比較理想,下載時間:199s

圖片圖片

多線程文件下載

如果服務(wù)器不限速的話,通常能夠把自己本地的帶寬給跑滿,那么使用單線程下載就夠了,但是如果遇到服務(wù)器限速,下載速度遠小于自己本地的帶寬,那么可以考慮使用多線程下載。多線程我們使用CompletableFuture(可以參考之前的文章 《CompletableFuture讓你的代碼免受阻塞之苦》)。

實現(xiàn)多線程文件下載的基本流程:

  1. 首先我們通過Http協(xié)議的Head方法獲取到文件的總大小
  2. 然后根據(jù)設(shè)置的線程數(shù)均分文件的大小,計算每個線程的下載的字節(jié)數(shù)據(jù)開始位置和結(jié)束位置
  3. 開啟線程,設(shè)置HTTP請求頭Range信息,開始下載數(shù)據(jù)到臨時文件
  4. 下載完成后把每個線程下載完成的臨時文件合并成一個文件

完成代碼如下:

圖片

  1. 開啟30個線程測試下載819M的idea

圖片圖片圖片

從執(zhí)行的結(jié)果上來看,因為開啟了30個線程同時在下載,內(nèi)存的占用要比單線程消耗的多,但是也在接受范圍內(nèi),下載時間:81s,速度提升2.5倍,這是因為idea的下載服務(wù)器沒有限速,本次多線程速度的提升僅僅是在充分的壓榨本地的帶寬,所以提示的幅度不大。

單線程下載和對線程下載對比測試

因為云盤對單個線程的下載速度做了限制,大概是在100kb,所以我們使用云盤的下載鏈接,來測試多線程和單線程的下載速度。

  1. 測試云盤中 46M 的文件的下載速度,自己本地最大下載速度 2M

  2. 獲取文件的下載地址

圖片

?

注意:從瀏覽器中獲取的鏈接需要先使用URLDecode解碼,否則下載會失敗,并且云盤文件的下載鏈接是有時效性的,過期后就不能在下載,需要重新生成下載鏈接

?

測試單線程下載文件

圖片

執(zhí)行的結(jié)果可以看出,云盤對單線程的下載限速真的是喪心病狂, 46M的文件下載需要耗時:600s

測試多線程下載文件

為了充分的壓榨網(wǎng)速,找出最合適的線程數(shù),所以測試了不同線程數(shù)的下載速度

線程數(shù)下載總耗時
1060s
2030s
3021s
4015s
5013s

從測試的結(jié)果上來看,對于自己的運行環(huán)境把線程數(shù)設(shè)置在30個左右比較合適


我們的服務(wù)

Our Services

新聞資訊

News Center 更多 +

聯(lián)系我們

Contact Us

咨詢熱線:

86-592-5151555

地址: 廈門市集美區(qū)軟件園三期A3棟504室

QQ:1039899831

固話:86-592-5151555

手機:18020730588(賴先生)

官網(wǎng):m.haymarketdoctors.com

微信二維碼

Copyright © 2000-2021 m.haymarketdoctors.com

閩網(wǎng)文〔2018〕11518-507號 | 閩ICP備17022492號-1

游戲作品版權(quán)歸原作者享有,如無意之中侵犯了您的版權(quán),請您按照《版權(quán)保護投訴指引》來信告知,本網(wǎng)站將應(yīng)您的要求刪除。