2018-03-10 android多線程斷點下載實現(xiàn)

筆記如下
  • 什么是多線程下載?


    圖二 多線程下載的分析.jpg
  • 首先要獲得要下載文件的總大小并創(chuàng)建規(guī)定大小的空文件
                //拿到文件的大小
                int length = conn.getContentLength();
                
                //getPathName(path)是得到文件名稱
                File file = new File(getPathName(path));
                
                //首先創(chuàng)建規(guī)定大小的空文件
                RandomAccessFile raf = new RandomAccessFile(file, "rw");
                
                raf.setLength(length);
                
                raf.close();
  • 每條線程的下載的起始位置于結(jié)束位置公式
                //去啟動線程去下載文件
                //threadId :線程的id號
                //threadCount :開幾條線程----3條
                for (int threadId = 0; threadId < threadCount; threadId++) {
                    
                    int startIndex = threadId*blockSize;
                    int endIndex  = (threadId+1)*blockSize-1;
                    
                    if (threadId==(threadCount-1)) {
                        endIndex = length-1;
                    }
                    
                    
                    
                    //System.out.println("第"+threadId+"線程:下載 從"+startIndex +"~"+endIndex);
                    
                    //開啟線程下載
                    new DownloadFilePartThread(threadId, startIndex, endIndex).start();
                    
                }

  • 通過請求頭告訴服務器要下載的內(nèi)容
conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);

  • 將服務器傳回的數(shù)據(jù)寫到文件中
                    //拿到數(shù)據(jù)
                    InputStream in = conn.getInputStream();
                                    
                    //告訴從哪個位子開始寫
                    //raf.seek(startIndex);
                    
                    int len=0;
                    byte[] buf = new byte[1024];
                    while ((len=in.read(buf))>0) {
                        raf.write(buf,0,len);
                        
                        //將實時的位置記錄下來,方便下面緊接著去往文件中去寫
                        currentPostion=currentPostion+len;
                        //實現(xiàn)斷點下載
                        File info  = new File(threadId+".position");
                        
                        OutputStream out = new FileOutputStream(info);
                        
                        //以字符串的值記錄當前緩存的配置
                        //out.write(String.valueOf(currentPostion).getBytes());
                        out.write((currentPostion+"").getBytes());
                        out.close();
                        
                    }
  • 實現(xiàn)斷點下載
    斷點下載就是記錄上次下載停止的地方,設置為開始地方繼續(xù)下載------將停止地點記錄到文件中.
                        //將實時的位置記錄下來,方便下面緊接著去往文件中去寫
                        currentPostion=currentPostion+len;
                        //實現(xiàn)斷點下載
                        File info  = new File(threadId+".position");
                        
                        OutputStream out = new FileOutputStream(info);
                        
                        //以字符串的值記錄當前緩存的配置
                        //out.write(String.valueOf(currentPostion).getBytes());
                        out.write((currentPostion+"").getBytes());
                        out.close();

                //讀取之前已經(jīng)下載的地方,繼續(xù)下載
                File ilf  = new File(threadId+".position");
                
                if (ilf.exists() && ilf.length()>0) {
                    
                    BufferedReader br = new BufferedReader(new FileReader(ilf));
                    String vl = br.readLine();
                    
                    int alreadyWritePosition  = Integer.valueOf(vl);
                    //重新設置http請求頭,設置起始下載位置
                    conn.setRequestProperty("range", "bytes=" +alreadyWritePosition+"-"+endIndex);
                    //告訴從哪個位子開始寫
                    raf.seek(alreadyWritePosition);
                    
                    System.out.println("表示之前下載過");
                    
                }else{
                    
                    conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
                    //告訴從哪個位子開始寫
                    raf.seek(startIndex);
                    System.out.println("表示之前沒有下載過");
                }
  • 文件下載完成后,還要刪除記錄位置的文件
synchronized (NultiThreadDownload.class) {

                    currentRunningThread--;
                    if (currentRunningThread <= 0) {

                        // 將記錄下載位置的文件給刪掉

                        for (threadId = 0; threadId < threadCount; threadId++) {
                            File fff = new File(threadId + ".position");
                            
                            fff.renameTo(new File(threadId + ".position.finish"));
                            File fll = new File(threadId + ".position.finish");
                            fll.delete();
                        }
                    }
                }
  • 全部源碼
public class MultiThreadDownload {
    
    //規(guī)定用三條線程去下載
    private  static int  threadCount = 3;
    
    //線程計數(shù)器
    private static int currentRunningThread = 3;
    
    //private static String path="http://169.254.210.207:8080/file.txt";
    private static String path="http://169.254.210.207:8080/ff.exe";
    public static void main(String[] args){
        
        //1.向服務器發(fā)請求,拿到要下載的文件的長度是多少
        
        try {
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");
            
            int code = conn.getResponseCode();
            
            if (code == 200) {
                
                //拿到文件的大小
                int length = conn.getContentLength();
                
                //getPathName(path)是得到文件名稱
                //得到文件路徑
                File file = new File(getPathName(path));
                
                //首先創(chuàng)建規(guī)定大小的空文件
                RandomAccessFile raf = new RandomAccessFile(file, "rw");
                
                raf.setLength(length);
                
                raf.close();
                
                //每塊線程下載的平均大小
                int blockSize = length/threadCount;
                
                
                
                //去啟動線程去下載文件
                //threadId :線程的id號
                //threadCount :開幾條線程----3條
                for (int threadId = 0; threadId < threadCount; threadId++) {
                    
                    int startIndex = threadId*blockSize;
                    int endIndex  = (threadId+1)*blockSize-1;
                    
                    if (threadId==(threadCount-1)) {
                        endIndex = length-1;
                    }
                    
                    
                    
                    //System.out.println("第"+threadId+"線程:下載 從"+startIndex +"~"+endIndex);
                    
                    
                    new DownloadFilePartThread(threadId, startIndex, endIndex).start();
                    
                }
                
                
            }
            
            
            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        
        
        
    }
    
    
    
    
    private static class DownloadFilePartThread extends Thread{
        
        //線程的id號
        private int threadId;
        
        //線程的開始位置
        private int startIndex;
        
        //線程的結(jié)束位置
        private int endIndex;
        
        //當前線程下載位置
        private int currentPostion;
        
        
        
        public DownloadFilePartThread(int threadId,int startIndex,int endIndex){
            this.threadId  = threadId;
            this.startIndex  = startIndex;
            this.endIndex  = endIndex;
            currentPostion = startIndex;
        }
        
        @Override
        public void run() {
            // TODO Auto-generated method stub

            //干下載耗時的事
            System.out.println("第"+threadId+"線程:開始下載了.....   從"+startIndex +"~"+endIndex);
            
            
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                
                conn.setConnectTimeout(5000);
                conn.setRequestMethod("GET");
                
                //在多線程下載中只需要目標文件的一部分的數(shù)據(jù)
                //需要告訴服務器,要下載的內(nèi)容
                //需要設置http請求頭可以實現(xiàn)
                
                //startIndex ~ endIndex
                
                conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
                
                File file = new File(getPathName(path));
                
                RandomAccessFile raf = new RandomAccessFile(file, "rw");
                
                //讀取之前已經(jīng)下載的地方,繼續(xù)下載
                File ilf  = new File(threadId+".position");
                
                if (ilf.exists() && ilf.length()>0) {
                    
                    BufferedReader br = new BufferedReader(new FileReader(ilf));
                    String vl = br.readLine();
                    
                    int alreadyWritePosition  = Integer.valueOf(vl);
                    //重新設置http請求頭,設置起始下載位置
                    conn.setRequestProperty("range", "bytes=" +alreadyWritePosition+"-"+endIndex);
                    //告訴從哪個位子開始寫
                    raf.seek(alreadyWritePosition);
                    
                    System.out.println("表示之前下載過");
                    
                }else{
                    
                    conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
                    //告訴從哪個位子開始寫
                    raf.seek(startIndex);
                    System.out.println("表示之前沒有下載過");
                }
                
                
                
                //多線程下載返回----206
                int code = conn.getResponseCode();
                if (code == 206) {
                    
                    //拿到數(shù)據(jù)
                    InputStream in = conn.getInputStream();
                                    
                    //告訴從哪個位子開始寫
                    //raf.seek(startIndex);
                    
                    int len=0;
                    byte[] buf = new byte[1024];
                    while ((len=in.read(buf))>0) {
                        raf.write(buf,0,len);
                        
                        //將實時的位置記錄下來,方便下面緊接著去往文件中去寫
                        currentPostion=currentPostion+len;
                        //實現(xiàn)斷點下載
                        File info  = new File(threadId+".position");
                        
                        OutputStream out = new FileOutputStream(info);
                        
                        //以字符串的值記錄當前緩存的配置
                        //out.write(String.valueOf(currentPostion).getBytes());
                        out.write((currentPostion+"").getBytes());
                        out.close();
                        
                    }
                    
                    
                    
                    in.close();
                    raf.close();
                }
                
                System.out.println("第"+threadId+"線程:下載結(jié)束了..... ");
                
                //等到線程完成后再去刪文件
                //弄一個計數(shù)器,記住總共有多少線程下載,每當一個線程下載后就-1
                //當計數(shù)器小于0或等于0的時候,就說明沒有線程在下載了,就刪除記錄下位置的文件
                
                synchronized (MultiThreadDownload.class) {

                    currentRunningThread--;
                    if (currentRunningThread <= 0) {

                        // 將記錄下載位置的文件給刪掉

                        for (threadId = 0; threadId < threadCount; threadId++) {
                            File fff = new File(threadId + ".position");
                            
                            fff.renameTo(new File(threadId + ".position.finish"));
                            File fll = new File(threadId + ".position.finish");
                            fll.delete();
                        }
                    }
                }
                
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            
            
        }
        
    }
    
    
    public static String getPathName(String path){
        
        int index = path.lastIndexOf("/");
        
        return path.substring(index+1);
        
        
    }

}
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凶朗,一起剝皮案震驚了整個濱河市尘执,隨后出現(xiàn)的幾起案子苛吱,更是在濱河造成了極大的恐慌,老刑警劉巖筛婉,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犯建,死亡現(xiàn)場離奇詭異,居然都是意外死亡顿肺,警方通過查閱死者的電腦和手機遮晚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門性昭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人县遣,你說我怎么就攤上這事糜颠。” “怎么了萧求?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵其兴,是天一觀的道長。 經(jīng)常有香客問我夸政,道長元旬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮法绵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘酪碘。我一直安慰自己朋譬,他們只是感情好,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布兴垦。 她就那樣靜靜地躺著徙赢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪探越。 梳的紋絲不亂的頭發(fā)上狡赐,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音钦幔,去河邊找鬼枕屉。 笑死,一個胖子當著我的面吹牛鲤氢,可吹牛的內(nèi)容都是我干的搀擂。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼卷玉,長吁一口氣:“原來是場噩夢啊……” “哼哨颂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起相种,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤威恼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后寝并,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箫措,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年衬潦,在試婚紗的時候發(fā)現(xiàn)自己被綠了蒂破。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡别渔,死狀恐怖附迷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哎媚,我是刑警寧澤喇伯,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站拨与,受9級特大地震影響稻据,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一捻悯、第九天 我趴在偏房一處隱蔽的房頂上張望匆赃。 院中可真熱鬧,春花似錦今缚、人聲如沸算柳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞬项。三九已至,卻和暖如春何荚,著一層夾襖步出監(jiān)牢的瞬間囱淋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工餐塘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留妥衣,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓戒傻,卻偏偏與公主長得像称鳞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稠鼻,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

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