1. 程式人生 > 程式設計 >Java多執行緒檔案分片下載實現的示例程式碼

Java多執行緒檔案分片下載實現的示例程式碼

多執行緒下載介紹

多執行緒下載技術是很常見的一種下載方案,這種方式充分利用了多執行緒的優勢,在同一時間段內通過多個執行緒發起下載請求,將需要下載的資料分割成多個部分,每一個執行緒只負責下載其中一個部分,然後將下載後的資料組裝成完整的資料檔案,這樣便大大加快了下載效率。常見的下載器,迅雷,QQ旋風等都採用了這種技術。

分片下載

所謂分片下載就是要利用多執行緒的優勢,將要下載的檔案一塊一塊的分配到各個執行緒中去下載,這樣就極大的提高了下載速度。

技術難點

並不能說是什麼難點,只能說沒接觸過不知道罷了。

1、如何請求才能拿到資料的特定部分,而非全部?

可以在HTTP請求頭中加入Range來標識資料的請求範圍/區間,從HTTP/1.1開始可用。

基本用法:

Range: bytes=10-:取第10個位元組及後所有資料。

Range: bytes=40-100:取第40個位元組到第100個位元組之間的資料。

這樣我們就能拿到特定部分的資料了,斷點續傳也可以用這個來實現。

PS:0為開始點。

2、分片後某執行緒下載時如何寫出?

思路1:等所有下載完成後進行統一彙總整理然後再一次性寫出。

這簡直是最笨的思路了,如果檔案過大全部拉到記憶體中,豈不涼涼。

思路2:下載採用多執行緒,寫出時採取資料前後順序排隊寫出。

也就是說多執行緒下載,單執行緒輸出,某種程度解決了記憶體佔用問題,不過效率基本不理想。

思路3:要說還是API香,老大哥Java給我們提供了一個類叫做RandomAccessFile。

這個類可以進行隨機檔案讀寫,其中有一個seek函式,可以將指標指向任意位置,然後進行讀寫。什麼意思呢,舉個栗子:假如我們開了30個執行緒,首先第一個下載完成的是執行緒X,它下載的資料範圍是4000-9000,那麼這時我們呼叫seek函式將指標撥動到4000,然後呼叫它的write函式將byte寫出,這時4000之前都是NULL,4000之後就是我們插入的資料。這樣就可以實現多執行緒下載和本地寫入了。

具體實現

一個分片下載類,我們需要建立多個物件來進行下載。

public class UnitDownloader implements Runnable {
  private int from;
  private int to;
  private File target;
  private String uri;
  private int id;

  public UnitDownloader(int from,int to,File target,String uri,int id) {
    this.from = from;
    this.to = to;
    this.target = target;
    this.uri = uri;
    this.id = id;
  }

  public int getFrom() {
    return from;
  }

  public int getTo() {
    return to;
  }

  @Override
  public void run() {
    //download and save data
    try {
      HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();
      connection.setRequestProperty("Range","bytes=" + from + "-" + to);
      connection.connect();
      int totalSize = connection.getContentLength();
      InputStream inputStream = connection.getInputStream();
      RandomAccessFile randomAccessFile = new RandomAccessFile(target,"rw");
      randomAccessFile.seek(from);
      byte[] buffer = new byte[1024 * 1024];
      int readCount = inputStream.read(buffer,buffer.length);
      while (readCount > 0) {
        totalSize -= readCount;
        System.out.println("分片:" + this.id + "的剩餘:" + totalSize);
        randomAccessFile.write(buffer,readCount);
        readCount = inputStream.read(buffer,buffer.length);
      }
      inputStream.close();
      randomAccessFile.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

分片下載管理器,主要就是拿到內容的總大小,將其分配給每一個UnitDownloader。這裡的threadCount函式可以再考慮優化一下。

public class MultipleThreadDownloadManager implements Runnable {
  private String uri;
  private File target;

  public MultipleThreadDownloadManager(String uri,File target) {
    this.target = target;
    this.uri = uri;
    if (target.exists() == false) {
      try {
        target.createNewFile();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * 開始下載
   */
  public void start() {
    new Thread(this).start();
  }


  /**
   * 根據檔案總大小計算執行緒數量
   *
   * @param totalSize
   * @return
   */
  public int threadCount(int totalSize) {
    if (totalSize < 30 * 2014 * 1024) {
      return 1;
    }
    return 30;
  }


  @Override
  public void run() {
    //獲取檔案總大小
    int totalSize = 0;
    try {
      HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();
      connection.connect();
      int contentLength = connection.getContentLength();
      totalSize = contentLength;
    } catch (IOException e) {
      e.printStackTrace();
    }
    //將檔案分片並分開下載
    int threadCount = threadCount(totalSize);
    int perThreadSize = totalSize / threadCount;//每一個執行緒分到的任務下載量
    int id = 0;
    int from = 0,to = 0;
    while (totalSize > 0) {
      id++;
      //計算分片
      if (totalSize < perThreadSize) {
        from = 0;
        to = totalSize;
      } else {
        from = totalSize;
        to = from + perThreadSize;
      }
      //開始下載
      UnitDownloader downloader = new UnitDownloader(from,to,target,uri,id);
      new Thread(downloader).start();
    }
  }
}

參考文獻

1、https://emacsist.github.io/2015/12/29/http-%E5%8D%8F%E8%AE%AE%E4%B8%AD%E7%9A%84range%E8%AF%B7%E6%B1%82%E5%A4%B4%E4%BE%8B%E5%AD%90/

2、https://blog.csdn.net/lyt_7cs1dn9/article/details/75105266

到此這篇關於Java多執行緒檔案分片下載實現的示例程式碼的文章就介紹到這了,更多相關Java多執行緒分片下載內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!