多線程斷點(diǎn)下載

實(shí)現(xiàn)斷點(diǎn)續(xù)傳的邏輯,就是把當(dāng)前線程下載的位置保存起來,下次下載的時(shí)候,就按照上次下載的位置繼續(xù)下載,就行。

多線程加速下載見上一篇博客:多線程加速下載

存儲(chǔ)當(dāng)前線程下載的位置,存放在一個(gè)txt文件中

                    int total = 0; //代表當(dāng)前線程下載的大小 
                    
                    while((len = in.read(buffer))!=-1){
                        raf.write(buffer, 0, len);
                        
                        total +=len;
                        //[8]實(shí)現(xiàn)斷點(diǎn)續(xù)傳 就是把當(dāng)前線程下載的位置 給存起來 下次再下載的時(shí)候 就是按照上次下載的位置繼續(xù)下載 就可以了
                        int currentThreadPosition =  startIndex + total;  //比如就存到一個(gè)普通的.txt文本中
                        
                        //[9]用來存當(dāng)前線程下載的位置 
                        RandomAccessFile raff = new RandomAccessFile(getFilename(path)+threadId+".txt", "rwd");
                        raff.write(String.valueOf(currentThreadPosition).getBytes());
                        raff.close();
                        
                        
                    }

改變當(dāng)前線程的開始位置

                //[4.0]如果中間斷過  繼續(xù)上次的位置 繼續(xù)下載   從文件中讀取上次下載的位置 
                
                File file =new File(getFilename(path)+threadId+".txt");
                if (file.exists() && file.length()>0) {
                    FileInputStream fis = new  FileInputStream(file);
                    BufferedReader bufr = new  BufferedReader(new InputStreamReader(fis));
                    String lastPositionn = bufr.readLine();  //讀取出來的內(nèi)容就是上一次下載的位置
                    int lastPosition = Integer.parseInt(lastPositionn);

                    
                    //[4.0.1]要改變一下 startIndex 位置
                    startIndex = lastPosition + 1;
                    
                    System.out.println("線程id::"+threadId + "真實(shí)下載的位置"+":"+startIndex+"-----"+endIndex);
                    
                    fis.close(); //關(guān)閉流 
                }
                
                //[4.1]設(shè)置一個(gè)請(qǐng)求頭Range (作用告訴服務(wù)器每個(gè)線程下載的開始位置和結(jié)束位置)
                conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);

所有線程下載完畢,刪除txt文本,線程同步的方式實(shí)現(xiàn)

                    //[10]把.txt文件刪除  每個(gè)線程具體什么時(shí)候下載完畢了 我們不知道 
                    
                    synchronized (DownLoadThread.class) {
                        runningThread--;
                        if (runningThread == 0) {
                            //說明所有的線程都執(zhí)行完畢了 就把.txt文件刪除
                            for (int i = 0; i < threadCount; i++) {
                                File  delteFile = new File(getFilename(path)+i+".txt");
                                delteFile.delete();
                            }
                            
                             
                            
                            
                        }
                    }

程序完整代碼;

public class MutilDownLoad {

    
    //[1]定義下載的路徑 
    private  static String path = "http://192.168.11.73:8080/feiq.exe";
    
    private static final int threadCount = 3; //假設(shè)開三個(gè)線程 
     
    private static int runningThread;  //代表當(dāng)前正在運(yùn)行的線程 
    
    
    
    public static void main(String[] args) {
        
        
        //[一 ☆☆☆☆]獲取服務(wù)器文件的大小   要計(jì)算每個(gè)線程下載的開始位置和結(jié)束位置
        
        try {

            //(1) 創(chuàng)建一個(gè)url對(duì)象 參數(shù)就是網(wǎng)址 
            URL url = new URL(path);
            //(2)獲取HttpURLConnection 鏈接對(duì)象
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //(3)設(shè)置參數(shù)  發(fā)送get請(qǐng)求
            conn.setRequestMethod("GET"); //默認(rèn)請(qǐng)求 就是get  要大寫
            //(4)設(shè)置鏈接網(wǎng)絡(luò)的超時(shí)時(shí)間 
            conn.setConnectTimeout(5000);
            //(5)獲取服務(wù)器返回的狀態(tài)碼 
            int code = conn.getResponseCode(); //200  代表獲取服務(wù)器資源全部成功  206請(qǐng)求部分資源    
            if (code == 200) {

                //(6)獲取服務(wù)器文件的大小
                int length = conn.getContentLength();
                
                //[6.1]把 線程的數(shù)量賦值給正在運(yùn)行的線程
                runningThread = threadCount;
                
                
                System.out.println("length:"+length);
                
                //[二☆☆☆☆ ] 創(chuàng)建一個(gè)大小和服務(wù)器一模一樣的文件 目的提前把空間申請(qǐng)出來 
                RandomAccessFile rafAccessFile = new RandomAccessFile(getFilename(path), "rw");
                rafAccessFile.setLength(length);
                
                //(7)算出每個(gè)線程下載的大小 
                int blockSize = length /threadCount;
                
                //[三☆☆☆☆  計(jì)算每個(gè)線程下載的開始位置和結(jié)束位置 ]
                for (int i = 0; i < threadCount; i++) {
                    int startIndex = i * blockSize;   //每個(gè)線程下載的開始位置 
                    int endIndex = (i+1)*blockSize - 1;
                    //特殊情況 就是最后一個(gè)線程 
                    if (i == threadCount - 1) {
                        //說明是最后一個(gè)線程 
                        endIndex = length - 1;
                        
                    }
                    
                    System.out.println("線程id:::"+i + "理論下載的位置"+":"+startIndex+"-----"+endIndex);
                    
                    //四 開啟線程去服務(wù)器下載文件 
                    DownLoadThread downLoadThread = new DownLoadThread(startIndex, endIndex, i);
                    downLoadThread.start();
                    
                }
                
                
                
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        
        
        
    }
    
    
    //定義線程去服務(wù)器下載文件  
    private static class DownLoadThread extends Thread{
        //通過構(gòu)造方法把每個(gè)線程下載的開始位置和結(jié)束位置傳遞進(jìn)來 
        
        private int startIndex;
        private int endIndex;
        private int threadId;
        public DownLoadThread(int startIndex,int endIndex,int threadId){
            this.startIndex = startIndex;
            this.endIndex  = endIndex;
            this.threadId = threadId;
        }
        
        @Override
        public void run() {
            //四  實(shí)現(xiàn)去服務(wù)器下載文件的邏輯  
            
            try {

                //(1) 創(chuàng)建一個(gè)url對(duì)象 參數(shù)就是網(wǎng)址 
                URL url = new URL(path);
                //(2)獲取HttpURLConnection 鏈接對(duì)象
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                //(3)設(shè)置參數(shù)  發(fā)送get請(qǐng)求
                conn.setRequestMethod("GET"); //默認(rèn)請(qǐng)求 就是get  要大寫
                //(4)設(shè)置鏈接網(wǎng)絡(luò)的超時(shí)時(shí)間 
                conn.setConnectTimeout(5000);
                
                
                //[4.0]如果中間斷過  繼續(xù)上次的位置 繼續(xù)下載   從文件中讀取上次下載的位置 
                
                File file =new File(getFilename(path)+threadId+".txt");
                if (file.exists() && file.length()>0) {
                    FileInputStream fis = new  FileInputStream(file);
                    BufferedReader bufr = new  BufferedReader(new InputStreamReader(fis));
                    String lastPositionn = bufr.readLine();  //讀取出來的內(nèi)容就是上一次下載的位置
                    int lastPosition = Integer.parseInt(lastPositionn);

                    
                    //[4.0.1]要改變一下 startIndex 位置
                    startIndex = lastPosition + 1;
                    
                    System.out.println("線程id::"+threadId + "真實(shí)下載的位置"+":"+startIndex+"-----"+endIndex);
                    
                    fis.close(); //關(guān)閉流 
                }
                
                //[4.1]設(shè)置一個(gè)請(qǐng)求頭Range (作用告訴服務(wù)器每個(gè)線程下載的開始位置和結(jié)束位置)
                conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
                
                //(5)獲取服務(wù)器返回的狀態(tài)碼 
                int code = conn.getResponseCode(); //200  代表獲取服務(wù)器資源全部成功  206請(qǐng)求部分資源 成功  
                if (code == 206) {
                    //[6]創(chuàng)建隨機(jī)讀寫文件對(duì)象 
                    RandomAccessFile raf = new RandomAccessFile(getFilename(path), "rw");
                    //[6]每個(gè)線程要從自己的位置開始寫 
                    raf.seek(startIndex);
                    
                    InputStream in = conn.getInputStream(); //存的是feiq.exe 
                    
                    //[7]把數(shù)據(jù)寫到文件中
                    int len = -1;
                    byte[] buffer = new byte[1024*1024];//1Mb
                    
                    int total = 0; //代表當(dāng)前線程下載的大小 
                    
                    while((len = in.read(buffer))!=-1){
                        raf.write(buffer, 0, len);
                        
                        total +=len;
                        //[8]實(shí)現(xiàn)斷點(diǎn)續(xù)傳 就是把當(dāng)前線程下載的位置 給存起來 下次再下載的時(shí)候 就是按照上次下載的位置繼續(xù)下載 就可以了
                        int currentThreadPosition =  startIndex + total;  //比如就存到一個(gè)普通的.txt文本中
                        
                        //[9]用來存當(dāng)前線程下載的位置 
                        RandomAccessFile raff = new RandomAccessFile(getFilename(path)+threadId+".txt", "rwd");
                        raff.write(String.valueOf(currentThreadPosition).getBytes());
                        raff.close();
                        
                        
                    }
                    raf.close();//關(guān)閉流  釋放資源
                    
                    System.out.println("線程id:"+threadId + "---下載完畢了");
                    
                    //[10]把.txt文件刪除  每個(gè)線程具體什么時(shí)候下載完畢了 我們不知道 
                    
                    synchronized (DownLoadThread.class) {
                        runningThread--;
                        if (runningThread == 0) {
                            //說明所有的線程都執(zhí)行完畢了 就把.txt文件刪除
                            for (int i = 0; i < threadCount; i++) {
                                File  delteFile = new File(getFilename(path)+i+".txt");
                                delteFile.delete();
                            }
                            
                             
                            
                            
                        }
                    }
                    
                    
                    
                    
                    
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            
            
            
        }
    }
    
    //獲取文件的名字    "http://192.168.11.73:8080/feiq.exe";
    public static String getFilename(String path){
        
        int start =  path.lastIndexOf("/")+1;
        return path.substring(start);
    }
    
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容