通过HTTP协议实现多线程下载
生活随笔
收集整理的這篇文章主要介紹了
通过HTTP协议实现多线程下载
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
1. 基本原理,每條線程從文件不同的位置開始下載,最后合并出完整的數(shù)據(jù)。?
2. 使用多線程下載的好處?
??? 下載速度快。為什么呢?很好理解,以往我是一條線程在服務(wù)器上下載。也就是說,對應(yīng)在服務(wù)器上,有一個我的下載線程存在。?
??? 這時候肯定不只我一個人在下載,服務(wù)器上肯定同時存在多條下載線程,在下載服務(wù)器資源。對于 CPU 來說,不可能實(shí)現(xiàn)并發(fā)執(zhí)行。?
??? CPU 會公平的為這些線程劃分時間片,輪流執(zhí)行,a線程十毫秒 , b線程十毫秒...?
??? 假設(shè)運(yùn)用了本文這種手法,意味著我的下載應(yīng)用,可以同時使用服務(wù)器端的任意多條線程同時下載(理論上).?
??? 假設(shè)這個線程數(shù)目是 50 條,本應(yīng)用就將更多的得到服務(wù)器 CPU 的照顧超過 50 倍.?
??? 但是總歸會受本地網(wǎng)絡(luò)速度的限制。?
3. 每條線程要負(fù)責(zé)下載的數(shù)據(jù)長度可以用 “下載數(shù)據(jù)的總長度” 除以 “參與下載的線程總數(shù)” 來計(jì)算。但是要考慮到不能整除的情況。?
??? 假設(shè)有 5 條線程參與下載,那么計(jì)算公式應(yīng)該為 :?
??????????? int block = 數(shù)據(jù)總長度%線程數(shù) == 0? 10/3 : 10/3+1; (不能整除,則加一)?
4. 和數(shù)據(jù)庫分頁查詢類型。每條線程需要知道自己從數(shù)據(jù)的什么位置開始下載,下載到什么位置為止。?
?? 首先,為每一個線程配備一個 id , id 從零開始,為 0 1 2 3...?
?? 開始位置:線程 id 乘以每條線程負(fù)責(zé)下載的數(shù)據(jù)長度.?
?? 結(jié)束位置:下一個線程開始位置的前一個位置。?
?? 如:?
????? int startPosition =? 線程id * 每條線程下載的數(shù)據(jù)長度?
????? int endPosition = (線程id + 1) * 每條線程下載的數(shù)據(jù)長度 -1;?
????????
5. HTTP 協(xié)議的 Range 頭可以指定從文件的什么位置開始下載,下載到什么位置結(jié)束。單位為 1byte?
?? Range:bytes=2097152-4194304 表示從文件的 2M 的位置開始下載,下載到 4M 處結(jié)束?
?? 假如 Range 指定要讀取到 文件的 5104389 的字節(jié)數(shù)位置,但是下載的文件本身只有 4104389 個長度。那么下載操作自動會在 4104389 處停止。?
?? 因此不會下載到多余的無效數(shù)據(jù).?
6. 另一個難題是如何按順序?qū)?shù)據(jù)寫往本地文件。因?yàn)?#xff0c;線程是同步執(zhí)行的,它們同時在往本地目標(biāo)文件寫入數(shù)據(jù)。
?? 而線程于線程之間寫入的數(shù)據(jù)并沒有按照下載數(shù)據(jù)本身的順序。若按照普通的 OutputStream 的寫入方式,最后的本地下載文件將失真。?
?? 于是我們將用到下面這個類:?
???? java.io.RandomAccessFile?
???? 因?yàn)榇祟愅瑫r實(shí)現(xiàn)了 DataOutput 和 DataInput 的方法。使他們同時具有寫入和讀取功能。?
???? 這個類仿佛存在一個類似文件指針的東西,可以隨意執(zhí)行文件的任意一個位置開始讀寫.?
???? 因此此類的實(shí)例支持對隨機(jī)訪問文件的讀取和寫入.?
?????
???? 例如:?
???????
Java代碼?? ????
??? 雖然,執(zhí)行完這段代碼后,我們還沒有向目標(biāo)文件 "1.txt" 寫入任何數(shù)據(jù)。但是如果此時查看其大小,已經(jīng)為 1kb 了。這是我們自己設(shè)置的大小。?
??? 這個操作類似于向這個文件存儲了一個大型的 byte 數(shù)組。這個數(shù)組將這個文件撐到指定大小。等待被填滿。?
??? 既然是這樣,好處就在于,我們可以通過 “索引” 隨機(jī)訪問此文件系統(tǒng)的某個部分。?
??? 例如,可能這個文件大小為 500?
??? 那么,我的業(yè)務(wù)需求可能需要第一次 從 300 位置開始寫數(shù)據(jù),寫到 350 為止。?
??? 第二次,我又從 50 開始寫數(shù)據(jù),寫到 100 為止。?
??? 總之,我不是 “一次性” 的 “按順序” 的將這個文件寫完。?
??? 那么,RandomAccessFile 可以支持這種操作。?
?????
???? API?
????? void setLength(long newLength)?
????????? Sets the length of this file. (設(shè)置文件的預(yù)計(jì)大小)?
????? void seek(long pos)?
????????? Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs.?
????????? 假設(shè)為這個方法傳入 1028 這個參數(shù),表示,將從文件的 1028 位置開始寫入。?
????? void write(byte[] b, int off, int len)?
????????? Writes len bytes from the specified byte array starting at offset off to this file.?
????? write(byte[] b)?
????????? Writes b.length bytes from the specified byte array to this file, starting at the current file pointer.?
????? void writeUTF(String str)?
????????? Writes a string to the file using modified UTF-8 encoding in a machine-independent manner.?
?????? String readLine()?
????????? Reads the next line of text from this file.?
????? 實(shí)驗(yàn)代碼:?
?????
Java代碼??
????
??? 以上實(shí)驗(yàn)成功,雖然我們寫入字符串的順序?yàn)?"2"、"1"、"3",但是因?yàn)樵O(shè)置了文件偏移量的關(guān)系,文件最終保存的數(shù)據(jù)為 : 123?
??? 另一個疑問,寫完這三個數(shù)據(jù),文件的大小已經(jīng)為 3 個字節(jié)大小了。已經(jīng)撐滿了寫入的數(shù)據(jù),那么我們繼續(xù)往里面放數(shù)據(jù)會有什么效果??
????
??? /* 向超出大小的第四個字節(jié)位置寫入數(shù)據(jù) */?
??? accessFile.seek(3);?
??? accessFile.write("400".getBytes());?
????
??? 以上代碼無論 seek 方法指定的文件指針偏移量以及存入的數(shù)據(jù),都已經(jīng)超出了最開始為文件設(shè)定的 3 個字節(jié)的大小。?
??? 按照我的猜測,至少 “accessFile.seek(3)” 位置會拋出 "ArrayIndexOutOfBoundsException" 異常,表示下標(biāo)越界。?
??? 而,單獨(dú)執(zhí)行 "accessFile.write("400".getBytes())" 應(yīng)該可以成功。因?yàn)檫@個需求屬于合理的,應(yīng)該有執(zhí)行它的機(jī)制。?
??? 實(shí)驗(yàn)結(jié)果是兩句代碼都是成功的。貌似是說明,文件隱含的大型的字節(jié)數(shù)組,可以自動撐大。?
????
??? 但是要注意的問題是,必須要保證所設(shè)定的文件大小的每一個位置都具有合法的數(shù)據(jù),至少不能為空。?
??? 例如:?
??????? /* 向第三個位置寫入 '3'? */?
??????? accessFile.seek(2);?
??????? accessFile.write("3".getBytes());?
????????
??????? accessFile.seek(5);?
??????? accessFile.write("400".getBytes());?
??? 那么結(jié)合之前的代碼,最后的結(jié)果為:?
??????? 123口口400?
??? 在空白的兩個位置處出現(xiàn)了亂碼。這是理所應(yīng)當(dāng)?shù)摹?
????
??? 另外,假設(shè)我們?yōu)槲募付艘话賯€長度:?
??????? accessFile.setLength(100);?
??? 而,實(shí)際上,我們只為其前五個位置設(shè)置了值。那么理所當(dāng)然的是,文件保存的數(shù)據(jù),最后會綴上 95 個亂碼。?
????
7. 準(zhǔn)備工作應(yīng)該十分充分了。接下來上代碼。?
???
Java代碼?? 轉(zhuǎn)載于:https://www.cnblogs.com/bigben0123/p/4527413.html
總結(jié)
以上是生活随笔為你收集整理的通过HTTP协议实现多线程下载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高血压的标准
- 下一篇: HDU 3932 模拟退火