java多線程斷點下載

最近閑的比較蛋疼烂斋,原本軟件計劃招20個人的葫笼,現(xiàn)在14個人的團隊都是一半在打醬油深啤,這跟全國經(jīng)濟形勢有關(guān),我們也沒太大辦法路星,所以最近是啥都看看溯街。昨天晚上看了一個多線程斷點下載,今天就用Java實現(xiàn)了一遍洋丐。
基本思路:
1呈昔、通過HttpURLConnection獲取網(wǎng)絡(luò)資源,得到資源大小等一些信息友绝,
2堤尾、在本地創(chuàng)建一個和通過網(wǎng)絡(luò)獲取的資源同樣大小的文件(目的是為了存放下載的資源)
3、分配每個線程下載文件的開始位置和結(jié)束位置(下載時記錄每個線程下載的起始位置迁客,方便停止后能繼續(xù)下載)
4郭宝、開啟線程去下載。

下面開始實現(xiàn)

private static int threadCount = 3;//開啟3個線程
    private static int blockSize = 0;//每個線程下載的大小
    private static int runningTrheadCount = 0;//當(dāng)前運行的線程數(shù)
    private static String path = "http://sw.bos.baidu.com/sw-search-sp/software/09d9bc67eab07/QQ_8.7.19075.0_setup.exe";
    private static String filename ="QQ_8.7.19075.0_setup.exe";
    /**
     * @param args
     */
    public static void main(String[] args) {

        try{
            //1.請求url地址獲取服務(wù)端資源的大小
            URL url = new URL(path);
            HttpURLConnection openConnection = (HttpURLConnection) url.openConnection();
            openConnection.setRequestMethod("GET");
            openConnection.setConnectTimeout(5*1000);

            int code = openConnection.getResponseCode();
            if(code == 200){
                //獲取資源的大小
                int filelength = openConnection.getContentLength();
                if(filelength==-1){
                    filelength=1024*1024*60;
                }
                System.out.println("filelength="+filelength);
                //2.在本地創(chuàng)建一個與服務(wù)端資源同樣大小的一個文件(占位)
                RandomAccessFile randomAccessFile = new RandomAccessFile(filename, "rw");
                randomAccessFile.setLength(filelength);//設(shè)置隨機訪問文件的大小

                //3.要分配每個線程下載文件的開始位置和結(jié)束位置哲泊。
                blockSize = filelength/threadCount;//計算出每個線程理論下載大小
                for(int threadId =0 ;threadId < threadCount;threadId++){
                    int startIndex =  threadId * blockSize;//計算每個線程下載的開始位置
                    int endIndex = (threadId+1)*blockSize -1;//計算每個線程下載的結(jié)束位置
                    //如果是最后一個線程剩蟀,結(jié)束位置需要單獨計算
                    if(threadId == threadCount-1){
                        endIndex = filelength -1;
                    }

                    //4.開啟線程去執(zhí)行下載
                    new DownloadThread(threadId, startIndex, endIndex).start();


                }


            }


        }catch (Exception e) {
            e.printStackTrace();
        }

     }

上面代碼就是按照基本思路來寫的 首先通過請求url獲取網(wǎng)絡(luò)的資源催蝗,并設(shè)置為get請求切威,超時時間為5S 正常連接后獲取資源的大小,這里要注意一下丙号,在百度里面輸入”qq下載“ 點立即下載

1.png

得到的下載連接為https://www.baidu.com/link?url=V4gOxFgauy-GHJGTah_Os4obVwMcRqSdtCT3zL6Lxyzg5tMfIMqK7OW_WIr8cUasrc-h9fDypXCPa2EYE8e1242fYJshFLA1BYdPKIu2KWy&wd=&eqid=b7133a01000210550000000658008fce
用chrome帶的抓包工具發(fā)現(xiàn)

2.png
3.png

會有三個連接而我們用直接點擊下載得到的連接其中沒有Content-Length這一項先朦,因此當(dāng)調(diào)用openConnection.getContentLength();時返回值為-1缰冤, 看網(wǎng)上都說設(shè)置setRequestProperty(“Accept-Encoding”, “identity”); 就可以了,可是試了下并沒什么卵用 我不是搞網(wǎng)絡(luò)的出身喳魏,所以搞了個簡單的方法棉浸,用下面的地址獲取資源。就是抓的包最下面一個的URL地址
http://sw.bos.baidu.com/sw-search-sp/software/84b5fcf50a3de/QQ_8.7.19083.0_setup.exe

現(xiàn)在可以正確的獲取網(wǎng)絡(luò)資源的大小了刺彩。
繼續(xù) 創(chuàng)建一個RandomAccessFile類型的文件迷郑,之所以要用這個類創(chuàng)建文件,是為了為后面的斷點續(xù)傳做準備的创倔,看它名字也知道它 它可以隨機的開始寫入文件的位置嗡害,這個類功能非常強大,這里只用了它的randomAccessFile.seek(lastPostion)方法畦攘。
接著 就該分配線程 并確定每個線程的開始和結(jié)束位置了 這個沒什么難的霸妹,就是確定用幾個線程去下載,每個線程下載多少知押,隨你怎么分配了叹螟,只要他們連起來還是資源的大小,并且是連續(xù)的就行台盯。
接下來就是開啟線程去下載了罢绽。

public static class DownloadThread  extends Thread{


        private int threadId;
        private int startIndex;
        private int endIndex;
        private int lastPostion;
        public DownloadThread(int threadId,int startIndex,int endIndex){
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        @Override
        public void run() {
            synchronized (DownloadThread.class) {
                
                runningTrheadCount = runningTrheadCount +1;//開啟一線程,線程數(shù)加1
            }
                
            //分段請求網(wǎng)絡(luò)連接静盅,分段保存文件到本地
            try{
                URL url = new URL(path);
                HttpURLConnection openConnection = (HttpURLConnection) url.openConnection();
                openConnection.setRequestMethod("GET");
                openConnection.setConnectTimeout(5*1000);
                
                
                System.out.println("理論上下載:  線程:"+threadId+"有缆,開始位置:"+startIndex+";結(jié)束位置:"+endIndex);
                
                //讀取上次下載結(jié)束的位置,本次從這個位置開始直接下載。
                File file2 = new File(threadId+".txt");
                if(file2.exists()){
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file2)));
                    String lastPostion_str = bufferedReader.readLine();
                    lastPostion = Integer.parseInt(lastPostion_str);//讀取文件獲取上次下載的位置
                    
                    //設(shè)置分段下載的頭信息温亲。  Range:做分段數(shù)據(jù)請求用的棚壁。
                    openConnection.setRequestProperty("Range", "bytes="+lastPostion+"-"+endIndex);//bytes:0-500:請求服務(wù)器資源中0-500之間的字節(jié)信息  501-1000:
                    System.out.println("實際下載1:  線程:"+threadId+",開始位置:"+lastPostion+";結(jié)束位置:"+endIndex);
                    bufferedReader.close();
                }else{
                    
                    lastPostion = startIndex;
                    //設(shè)置分段下載的頭信息栈虚。  Range:做分段數(shù)據(jù)請求用的袖外。
                    openConnection.setRequestProperty("Range", "bytes="+lastPostion+"-"+endIndex);//bytes:0-500:請求服務(wù)器資源中0-500之間的字節(jié)信息  501-1000:
                    System.out.println("實際下載2:  線程:"+threadId+",開始位置:"+lastPostion+";結(jié)束位置:"+endIndex);
                }
                
                
                
                System.out.println("getResponseCode"+openConnection.getResponseCode() );
                
                
                
                if(openConnection.getResponseCode() == 206){//200:請求全部資源成功魂务, 206代表部分資源請求成功
                    InputStream inputStream = openConnection.getInputStream();
                    //請求成功將流寫入本地文件中曼验,已經(jīng)創(chuàng)建的占位那個文件中
                    
                    RandomAccessFile randomAccessFile = new RandomAccessFile(filename, "rw");
                    randomAccessFile.seek(lastPostion);//設(shè)置隨機文件從哪個位置開始寫。
                    //將流中的數(shù)據(jù)寫入文件
                    byte[] buffer = new byte[1024];
                    int length = -1;
                    int total = 0;//記錄本次線程下載的總大小
                    
                    while((length= inputStream.read(buffer)) !=-1){
                        randomAccessFile.write(buffer, 0, length);
                        
                        total = total+ length;
                        //去保存當(dāng)前線程下載的位置粘姜,保存到文件中
                        int currentThreadPostion = lastPostion + total;//計算出當(dāng)前線程本次下載的位置
                        //創(chuàng)建隨機文件保存當(dāng)前線程下載的位置
                        File file = new File(threadId+".txt");
                        RandomAccessFile accessfile = new RandomAccessFile(file, "rwd");
                        accessfile.write(String.valueOf(currentThreadPostion).getBytes());
                        accessfile.close();
                        
                        
                        
                    }
                    //關(guān)閉相關(guān)的流信息
                    inputStream.close();
                    randomAccessFile.close();
                    
                    System.out.println("線程:"+threadId+"鬓照,下載完畢");
                    
                    
                    
                    //當(dāng)所有線程下載結(jié)束,刪除存放下載位置的文件孤紧。
                    synchronized (DownloadThread.class) {
                        runningTrheadCount = runningTrheadCount -1;//標志著一個線程下載結(jié)束豺裆。
                        if(runningTrheadCount == 0 ){
                            System.out.println("所有線程下載完成");
                            for(int i =0 ;i< threadCount;i++){
                                File file = new File(i+".txt");
                                System.out.println(file.getAbsolutePath());
                                file.delete();
                            }
                        }
                        
                    }
                
                    
                }
                

            }catch (Exception e) {
                e.printStackTrace();
            }



            super.run();
        }

    }
    

上面代碼主要做了4 件事
1、設(shè)置分段下載的頭信息;
2臭猜、分段下載網(wǎng)絡(luò)資源
3躺酒、當(dāng)中斷時把當(dāng)前各個線程當(dāng)前下載的位置分別保存到一個臨時文件中
4、下載完成后把臨時文件刪除 上面代碼中都給出了詳細的注釋

其中有一點要注意
openConnection.setRequestProperty("Range", "bytes="+lastPostion+"-"+endIndex);
如果"bytes=格式不對的話會導(dǎo)致設(shè)置不成功蔑歌,返回的將不是部分資源的返回碼
另一個要說明的就是randomAccessFile.seek(startThread);是設(shè)置各個線程下載的開始位置
現(xiàn)在的開源項目xutils也可以實現(xiàn)多線程斷點下載 不過還是附上demo吧

https://github.com/solary2014/Muchdownload
http://download.csdn.net/detail/asd1031/9654085

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末羹应,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子次屠,更是在濱河造成了極大的恐慌园匹,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劫灶,死亡現(xiàn)場離奇詭異偎肃,居然都是意外死亡,警方通過查閱死者的電腦和手機浑此,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門累颂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凛俱,你說我怎么就攤上這事紊馏。” “怎么了蒲犬?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵朱监,是天一觀的道長。 經(jīng)常有香客問我原叮,道長赫编,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任奋隶,我火速辦了婚禮擂送,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唯欣。我一直安慰自己嘹吨,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布境氢。 她就那樣靜靜地躺著蟀拷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萍聊。 梳的紋絲不亂的頭發(fā)上问芬,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音寿桨,去河邊找鬼此衅。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的炕柔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼媒佣,長吁一口氣:“原來是場噩夢啊……” “哼匕累!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起默伍,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤欢嘿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后也糊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炼蹦,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年狸剃,在試婚紗的時候發(fā)現(xiàn)自己被綠了掐隐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡钞馁,死狀恐怖虑省,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情僧凰,我是刑警寧澤探颈,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站训措,受9級特大地震影響伪节,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绩鸣,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一怀大、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧呀闻,春花似錦叉寂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至局服,卻和暖如春钓瞭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背淫奔。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工山涡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓鸭丛,卻偏偏與公主長得像竞穷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鳞溉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 什么是多線程下載 舉例: 一個裝有水的水桶(要下載的資源)瘾带,出水口(下載線程),出水口越多熟菲,水流的越快看政。即多個線程...
    ccplay5grate閱讀 275評論 0 2
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,527評論 25 707
  • 1.普通單線程下載文件: 直接使用URLConnection.openStream();打開網(wǎng)絡(luò)輸入流呆贿,然后將流寫...
    JuSong閱讀 2,574評論 2 10
  • 早晨早早出門嚷兔,開車上班。太陽已升起做入,露出紅撲撲笑臉谴垫,照的世界金燦燦的。今年倒春寒母蛛,寒流賴著不走翩剪,非要和春姑娘糾纏,...
    永遠是我閱讀 700評論 4 4
  • 有叔叔阿姨幫忙彩郊,同學(xué)們干的又快又好前弯。參與實踐活動讓孩子們感受到老師在開學(xué)初為他們所做的一切,體會老師的辛苦秫逝。再次感...
    孩兒王閱讀 206評論 0 0