天天看點

多線程斷點續傳要點

    • 定義
    • 作用
    • 斷點續傳支援
    • 下載下傳
    • 拓展

定義

首先我們要明白多線程斷點續傳是什麼,我看到有人了解為開了個線程去下載下傳支援斷點續傳的檔案就是多線程斷點續傳了,其實不然.所謂多線程斷點續傳是指開多個線程去分段下同一個檔案.

作用

斷點續傳:假如下載下傳大檔案不支援斷點續傳,使用者網絡一不穩定,或者切換網絡,隻要一失敗又得從頭開始下載下傳

多線程斷點續傳:如有個1200KB的檔案,伺服器下發單個連接配接每秒下載下傳速度上限為200KB, 用戶端網絡下載下傳速度為600KB/S.在這種場景下,假如我們這裡不考慮其他因素,單線程去下載下傳最少6s. 開啟3個線程去分段下載下傳這個檔案2s就可以下完.

斷點續傳支援

當然一般我們的項目都是已經知道伺服器給的檔案是否支援,是以大多時候不需要去判斷
           

方式一(推薦)

根據connection.getHeaderField(“Accept-Ranges”)的值來判斷

代碼片段

HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(Constants.CONNECT_TIME);
            connection.setReadTimeout(Constants.READ_TIME);
            int responseCode = connection.getResponseCode();
            int contentLength = connection.getContentLength();
            boolean isSupportRange = false;
            if (responseCode == HttpURLConnection.HTTP_OK) {
                String ranges = connection.getHeaderField("Accept-Ranges");
                if ("bytes".equals(ranges)) {
                    isSupportRange = true;
                }
            }
           

方式一(有風險)

設定range頭connection.setRequestProperty(“Range”, “bytes=0-” + Integer.MAX_VALUE);然後根據

if (responseCode == HttpURLConnection.HTTP_PARTIAL)來判斷,當檔案大概達到4G的時候,Integer.MAX_VALUE的值是完全不夠的,是以會導緻問題.

代碼片段

HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Range", "bytes=0-" + Integer.MAX_VALUE);
            connection.setConnectTimeout(Constants.CONNECT_TIME);
            connection.setReadTimeout(Constants.READ_TIME);
            int responseCode = connection.getResponseCode();
            int contentLength = connection.getContentLength();
            boolean isSupportRange = false;
            if (responseCode == HttpURLConnection.HTTP_PARTIAL) {  
                    isSupportRange = true;
            }
            connection.disconnect();
           

下載下傳

下載下傳需要注意分段的position問題,要明白是從0開始的.

比如600byte分3段(0~199,200~399,400~599)

代碼片段

long perSize = downloadEntry.totalLength / MAX_DOWNLOAD_THREAD;
        long startPosition;
        long endPosition;
        if (downloadEntry.progressMaps == null) {
            downloadEntry.progressMaps = new HashMap<>();
            for (int i = ; i < MAX_DOWNLOAD_THREAD; i++) {
                downloadEntry.progressMaps.put(i, );
            }
        }
        downloadThread = new DownloadThread[MAX_DOWNLOAD_THREAD];
        for (int i = ; i < MAX_DOWNLOAD_THREAD; i++) {
            startPosition = i * perSize + downloadEntry.progressMaps.get(i);
            if (i == MAX_DOWNLOAD_THREAD - ) {
                endPosition = downloadEntry.totalLength - ;
            } else {
                endPosition = (i + ) * perSize - ;
            }
            if (startPosition < endPosition) {
                downloadThread[i] = new DownloadThread(downloadEntry.url, i, startPosition, endPosition, this);
                executorService.execute(downloadThread[i]);
            }

        }
           

代碼片段

HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Range", "bytes=" + startPostion + "-" + endPosition);
            connection.setConnectTimeout(Constants.CONNECT_TIME);
            connection.setReadTimeout(Constants.READ_TIME);
            int responseCode = connection.getResponseCode();
            RandomAccessFile raf;
            int length;
            byte[] bytes = new byte[];
            InputStream is;
            File file = new File(path);
            if (responseCode == HttpURLConnection.HTTP_PARTIAL) {
                raf = new RandomAccessFile(file, "rw");
                raf.seek(startPostion);
                is = connection.getInputStream();
                while ((length = is.read(bytes)) != -) {
                    if (isPause) {
                        break;
                    }
                    raf.write(bytes, , length);
                    if (!file.exists()) {
                        throw new FileNotFoundException("Invalid file path");
                    }
                    downloadListener.onProgressChanged(index, length);
                }
                raf.close();
                is.close();
                connection.disconnect();
           

拓展

了解了這些,你完全可以自己撸一個多線程斷點續傳架構