首頁 > 軟體

Java實現多執行緒下載和斷點續傳

2022-06-24 10:01:03

java的多執行緒下載能夠明顯提升下載的速度,平時我們用的迅雷軟體之所以能夠下載那麼快,就是使用了多執行緒;當用戶在下載的過程中,有斷電或斷網的可能,當用戶再次點選下載時,應該讓使用者接著原來的進度進行下載,這可以節約使用者的流量,所以要用到斷點續傳的功能。下面是通過Java程式碼實現多執行緒下載和斷點續傳的詳細程式碼。

1、建立一個類,用於檔案的下載

package com.edu.thread;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
 
public class MultiDownload2 {
    static String path = "http://localhost:8080/wlan.zip";
    //開啟執行緒的數量
    static int threadCount = 6;
    //下載結束的執行緒數
    static int threadFinished = 0;
    public static void main(String[] args) {
    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("d://檔案測試", getFileName(path));
            //建立隨機儲存檔案大小,為了建立一個和原始檔大小相同的儲存區間
            RandomAccessFile raf = new RandomAccessFile(file, "rwd");
            //設定臨時檔案的大小,和伺服器檔案一模一樣
            raf.setLength(length);
            //計算每個執行緒下載的位元組數
            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) {
                    //length從0開始讀,所以length-1表示最後一個位元組
                    endIndex = length-1;
                }
                //列印三個執行緒的開始與結束位置
                System.out.println("執行緒"+i+"的開始和結束位置:"+startIndex+"----"+endIndex);
                //開啟執行緒,傳入執行緒ID,下載的開始位置和下載的結束位置
                new DownloadThread(i, startIndex, endIndex).start();;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /*
    * 獲取檔名
    */
    public static String getFileName(String path){
        int index=path.lastIndexOf("/");
        return path.substring(index + 1);
    }
}

2、建立另一個類,用於開啟子執行緒

package com.edu.thread;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
//新開啟一個執行緒,用於完成下載任務
class DownloadThread extends Thread{
 
    int thredId;
    int startIndex;
    int endIndex;
    
    public DownloadThread(int thredId, int startIndex, int endIndex) {
        super();
        this.thredId = thredId;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }
    
    public void run() {
        try {
        //下載進度檔案儲存的路徑和檔名
        File progressFile = new File("d://檔案測試",(thredId + ".txt"));
        //判斷儲存下載進度的臨時檔案是否存在,以便確定下載的開始位置
        if (progressFile.exists()) {
            FileInputStream fis = new FileInputStream(progressFile);
            BufferedReader bReader = new BufferedReader(new InputStreamReader(fis));
            //拿到臨時檔案中儲存的資料,並把此資料設定為新的開始位置
            int text = Integer.parseInt(bReader.readLine());
            startIndex = text;
            fis.close();
            }
            System.out.println("執行緒"+thredId+"的最終開始下載位置是:"+startIndex);
            
            URL url = new URL(MultiDownload2.path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            //設定請求資料的範圍
            conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
            //建立連線,狀態碼206表示請求部分資料成功,此時開始下載任務
            if (conn.getResponseCode()==206) {
                InputStream is = conn.getInputStream();
                //指定檔名和檔案路徑
                File file = new File(MultiDownload2.getFileName(MultiDownload2.path) );
                int len = 0;
                byte [] b = new byte[1024];
                //三個執行緒各自建立自己的隨機儲存檔案
                RandomAccessFile raf = new RandomAccessFile(file, "rwd");
                //設定資料從哪個位置開始寫入資料到臨時檔案
                raf.seek(startIndex);
                //設定當前執行緒下載的總位元組數
                int total = 0;
                long start = System.currentTimeMillis();
                
                //當下載意外停止時,記錄當前下載進度
                int currentPosition = startIndex;
                
                while ((len=is.read(b))!=-1) {
                    raf.write(b,0,len);
                    //列印當前執行緒下載的總位元組數
                    total += len;
                    /**
                    * 實現斷點續傳的功能
                    */
                    //RandomAccessFile主要用來存放下載的臨時檔案,可以用FileOutputStream代替
                    RandomAccessFile rafProgress = new RandomAccessFile(progressFile, "rwd");
                    //再次下載時的開始位置
                    currentPosition = startIndex + total;
                    //把下載進度寫進rafProgress臨時檔案,下一次下載時,就以這個值作為新的startIndex
                    rafProgress.write((currentPosition + "").getBytes());
                    //關流
                    rafProgress.close();
                    System.out.println("執行緒"+thredId+"已經下載了"+total);
                }
                raf.close();
                long end = System.currentTimeMillis();
                //列印執行緒下載檔案用時
                System.out.println("執行緒"+thredId+"下載檔案用時"+(end-start)+"ms");
                //列印執行緒的結束
                System.out.println("執行緒:"+thredId+" 下載結束了 !!!");
                //下載結束後,刪除所有的臨時檔案
                MultiDownload2.threadFinished ++;
                //使用同步語句塊,保證執行緒的安全性
                synchronized (MultiDownload2.path) {
                //如果這個條件成立,說明所有的執行緒下載結束
                if (MultiDownload2.threadFinished == MultiDownload2.threadCount) {
                    for (int i = 0; i < MultiDownload2.threadCount; i++) {
                        //刪除三個執行緒產生的臨時檔案
                        File temp = new File("d://檔案測試", i + ".txt");
                        temp.delete();
                    }
                    //保證三個執行緒的臨時檔案同時被刪除
                    MultiDownload2.threadFinished = 0;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com