所謂分片下載就是要利用多線程的優點,將要下載的文件一塊一塊的分配到各個線程中去下載,這樣就極大的提升了下載速度。java
並不能說是什麼難點,只能說沒接觸過不知道罷了。git
一、如何請求才能拿到數據的特定部分,而非所有?github
能夠在HTTP請求頭中加入Range來標識數據的請求範圍/區間,從HTTP/1.1開始可用。多線程
基本用法:dom
Range: bytes=10-
:取第10個字節及後全部數據。ide
Range: bytes=40-100
:取第40個字節到第100個字節之間的數據。函數
這樣咱們就能拿到特定部分的數據了,斷點續傳也能夠用這個來實現。優化
PS:0爲開始點。this
二、分片後某線程下載時如何寫出?.net
思路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, 0, buffer.length); while (readCount > 0) { totalSize -= readCount; System.out.println("分片:" + this.id + "的剩餘:" + totalSize); randomAccessFile.write(buffer, 0, readCount); readCount = inputStream.read(buffer, 0, 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(); } } }
二、https://blog.csdn.net/lyt_7cs1dn9/article/details/75105266