天天看点

多线程断点续传要点

    • 定义
    • 作用
    • 断点续传支持
    • 下载
    • 拓展

定义

首先我们要明白多线程断点续传是什么,我看到有人理解为开了个线程去下载支持断点续传的文件就是多线程断点续传了,其实不然.所谓多线程断点续传是指开多个线程去分段下同一个文件.

作用

断点续传:假如下载大文件不支持断点续传,用户网络一不稳定,或者切换网络,只要一失败又得从头开始下载

多线程断点续传:如有个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();
           

拓展

了解了这些,你完全可以自己撸一个多线程断点续传框架