Android學習(55) -- 帶斷點續傳的多線程下載(Java)

原理

將要已經下載進度保存到臨時文件中,例如:下載了3000字節的話,也就是保存了0~2999的位置,下次從3000的位置開始繼續下載,那麼把3000保存到臨時文件中即可。

對於多線程的話,我們也要分別進行記錄,有幾個線程則保存幾個文件,
例如
0號線程:下載了6000字節 記錄 0~5999 下次從6000字節開始
1號線程:下載了4000字節 記錄 0~3999 下次從4000字節開始
2號線程:下載了500字節 記錄 0~499 下次從500字節開始

計算:
例如:
下載區間爲100-300
已經下載120 即 100-219
下次下載應該從220開始下載。
即:newStartIdx =startInx + total

帶斷點續傳的多線程下載

  • 定義一個int變量記錄每條線程下載的數據總長度,然後加上該線程的下載開始位置,得到的結果就是下次下載時,該線程的開始位置,把得到的結果存入緩存文件

    //用來記錄當前線程總的下載長度
    int total = 0;
    while((len = is.read(b)) != -1){
        raf.write(b, 0, len);
        total += len;
        //每次下載都把新的下載位置寫入緩存文本文件
        RandomAccessFile raf2 = new RandomAccessFile(threadId + ".txt", "rwd");
        raf2.write((startIndex + total + "").getBytes());
        raf2.close();
    }
    
  • 下次下載開始時,先讀取緩存文件中的值,得到的值就是該線程新的開始位置

    FileInputStream fis = new FileInputStream(file);
    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
    String text = br.readLine();
    int newStartIndex = Integer.parseInt(text);
    //把讀到的值作爲新的開始位置
    startIndex = newStartIndex;
    fis.close();
    
  • 三條線程都下載完畢之後,刪除緩存文件

    RUNNING_THREAD--;
    if(RUNNING_THREAD == 0){
        for(int i = 0; i <= 3; i++){
            File f = new File(i + ".txt");
            f.delete();
        }
    }
    

    核心代碼

    public class MultiDownload {
    
        static int ThreadCount = 3;
        static int finishedThread = 0;
        //確定下載地址
        static String path = "http://192.168.1.13:8080/QQ.exe";
        public static void main(String[] args) {
    
            //發送get請求,請求這個地址的資源
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
    
                if(conn.getResponseCode() == 200){
                    //拿到所請求資源文件的長度
                    int length = conn.getContentLength();
    
                    File file = new File("local_qq.exe");
                    //生成臨時文件--佔用磁盤空間--可以防止磁盤空間不夠用
                    RandomAccessFile raf = new RandomAccessFile(file, "rwd");
                    //設置臨時文件的大小
                    raf.setLength(length);
                    raf.close();
                    //計算出每個線程應該下載多少字節
                    int size = length / ThreadCount;
    
                    for (int i = 0; i < ThreadCount; i++) {
                        //計算線程下載的開始位置和結束位置
                        int startIndex = i * size;
                        int endIndex = (i + 1) * size - 1;
                        //如果是最後一個線程,那麼結束位置寫死
                        if(i == ThreadCount - 1){
                            endIndex = length - 1;
                        }
    //                  System.out.println("線程" + i + "的下載區間是:" + startIndex + "---" + endIndex);
                        new DownLoadThread(startIndex, endIndex, i).start();
                    }
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    
    }
    class DownLoadThread extends Thread{
        int startIndex; //下載的開始位置
        int endIndex;   //下載的結束位置
        int threadId;   //下載的線程Id
    
        public DownLoadThread(int startIndex, int endIndex, int threadId) {
            super();
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.threadId = threadId;
        }
    
        @Override
        public void run() {
            //再次發送http請求,下載原文件
            try {
                File progressFile = new File(threadId + ".txt");
                //判斷進度臨時文件是否存在
                if(progressFile.exists()){
                    FileInputStream fis = new FileInputStream(progressFile);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    //從進度臨時文件中讀取出上一次下載的總進度,然後與原本的開始位置相加,得到新的開始位置
                    startIndex += Integer.parseInt(br.readLine());
                    fis.close();
                }
                System.out.println("線程" + threadId + "的下載區間是:" + startIndex + "---" + endIndex);
                HttpURLConnection conn;
                URL url = new URL(MultiDownload.path);
                conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
                //設置本次http請求所請求的數據的區間
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
    
                //請求部分數據,相應碼是206
                if(conn.getResponseCode() == 206){
                    //流裏此時只有1/3原文件的數據
                    InputStream is = conn.getInputStream();
                    byte[] b = new byte[1024];
                    int len = 0;
                    int total = 0;
                    //拿到臨時文件的輸出流
                    File file = new File("local_qq.exe");
                    RandomAccessFile raf = new RandomAccessFile(file, "rwd");
                    //把文件的寫入位置移動至startIndex
                    raf.seek(startIndex);
                    while((len = is.read(b)) != -1){
                        //每次讀取流裏數據之後,同步把數據寫入臨時文件
                        raf.write(b, 0, len);
                        total += len;
    //                  System.out.println("線程" + threadId + "下載了" + total);
    
                        //生成一個專門用來記錄下載進度的臨時文件
                        RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd");
                        //每次讀取流裏數據之後,同步把當前線程下載的總進度寫入進度臨時文件中
                        progressRaf.write((total + "").getBytes());
                        progressRaf.close();
                    }
                    System.out.println("線程" + threadId + "下載完畢-------------------");
                    raf.close();
    
                    MultiDownload.finishedThread++;
                    synchronized (MultiDownload.path) {
                        if(MultiDownload.finishedThread == MultiDownload.ThreadCount){
                            for (int i = 0; i < MultiDownload.ThreadCount; i++) {
                                File f = new File(i + ".txt");
                                f.delete();
                            }
                            MultiDownload.finishedThread = 0;
                        }
                    }
    
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
發佈了101 篇原創文章 · 獲贊 7 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章