Java利用分段续传,解决国内因防火墙导致下载国外资源慢的问题

想要下载某个国外资源,但是由于国内的防火墙,有些资源下载极慢,而且很容易失败。

我参考了 https://github.com/9crk/mget 的思路,先用他的写的 mget.sh 去下载,但还是失败(下载完成但md5比对有误,似乎数据有损坏)

别人的代码是他写的,具体实现细节我不清楚,但了解了他的思路,我就按此思路写了一个Java版的,并由此成功下载了想要的资源(开心)。完整代码如下,希望帮助得到跟我有同样困扰的人:

 

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;

/**
 * 多线程断点下载,用于解决下载国外资源慢的问题
 */
public class MutiDownload {
    
    /** 下载缓冲区大小 **/
    private static final int BUFFER_SIZE = 4096;
    /** 并发线程数 **/
    private static final int THREAD_NUM = 200;
    /** 每个连接的允许等待时间 **/
    private static final int TIMEOUT = 60000;
    
    /** 下载地址 **/
//    private static final String URL = "http://192.168.35.139:8080/MyWeb/image/Koala.jpg";
    private static final String URL = "https://jaist.dl.sourceforge.net/project/pywin32/pywin32/Build%20221/pywin32-221.win-amd64-py2.7.exe";
    /** 保存地址 **/
    private static final String SAVE_PATH = "D://test/pywin32-221.win-amd64-py2.7.exe";
    
    private static final Set<Integer> RUNING_LIST = new TreeSet<Integer>();
    
    public static void main(String[] args) {
        
        long totalLen = getLenth(URL);
        System.out.println("总数据长度:" + totalLen);
        
        // 删除旧的文件
        File pathName = new File(SAVE_PATH);
        if(pathName.exists()){
            pathName.delete();
        }
        RandomAccessFile randomAccessFile;
        try {
            randomAccessFile = new RandomAccessFile(SAVE_PATH, "rw");
            randomAccessFile.setLength(totalLen);
            
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        
        // 一个线程负责的下载量
        int oneSize = (int)(totalLen / THREAD_NUM);
        if(totalLen % oneSize != 0){
            oneSize++;
        }
        System.out.println("每个线程负责下载:" + oneSize);
        
        for (int i = 0; i < THREAD_NUM; i++) {
            int start = i * oneSize;
            int end = (i + 1) * oneSize;
            if(end >= totalLen){
                end = (int) totalLen;
            }
            synchronized (RUNING_LIST) {
                RUNING_LIST.add(i);
            }
            new DownloadThread(randomAccessFile, i, URL, start, end).start();
        }
        
    }

    /** 获取下载长度 **/
    public static final long getLenth(String url){
        long length = 0;
        try {
            HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection();
            conn.connect();
            int statusCode = conn.getResponseCode();
            System.out.println("getLenth, statusCode:" + statusCode);
            if(statusCode == HttpURLConnection.HTTP_OK){
                length = conn.getContentLengthLong();
            }
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return length;
    }
    
    /** 保存到文件 **/
    private synchronized static void save(RandomAccessFile randomAccessFile, byte [] data, int seek, int len) throws IOException{
        randomAccessFile.seek(seek);
        randomAccessFile.write(data, 0, len);
    }
    
    /** 打印下载进度信息 **/
    private synchronized static void printfDownloadInfo(int index){
        synchronized (RUNING_LIST) {
            RUNING_LIST.remove(index);
            System.out.println("线程 " + index + " 下载完成, 剩余" + Arrays.toString(RUNING_LIST.toArray()));
        }
    }
    
    /**
     * 下载线程
     */
    private static class DownloadThread extends Thread {
        
        private RandomAccessFile randomAccessFile;
        private int index;
        private String url;
        private int start, end;
        
        public DownloadThread(RandomAccessFile randomAccessFile, int index, String url, int start, int end) {
            super();
            this.randomAccessFile = randomAccessFile;
            this.index = index;
            this.url = url;
            this.start = start;
            this.end = end;
        }

        @Override
        public void run() {
            super.run();
            while (!doRun()) {
                System.out.println("线程 " + index + " 下载失败,执行重试");
            }
            printfDownloadInfo(index);
        }
        
        private boolean doRun(){
            HttpURLConnection conn = null;
            try {
                conn = (HttpURLConnection)new URL(url).openConnection();
                conn.setConnectTimeout(TIMEOUT);
                conn.setReadTimeout(TIMEOUT);
                conn.addRequestProperty("Range", "bytes=" + start +"-" + end);
                
                conn.connect();
                int statusCode = conn.getResponseCode();
                System.out.println("线程" + index + ", statusCode:" + statusCode);
                if(statusCode == HttpURLConnection.HTTP_OK || statusCode == HttpURLConnection.HTTP_PARTIAL){
                    int count = 0;
                    InputStream is = conn.getInputStream();
                    byte [] bs = new byte[BUFFER_SIZE];
                    int len;
                    while ((len = is.read(bs)) > 0) {
                        save(randomAccessFile, bs, start + count, len);
                        count += len;
                    }
                    is.close();
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            } finally {
                conn.disconnect();
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值