• 多线程断点续传


      http://wuhongyu.iteye.com/blog/869109

        功能很简单,就是启动多个线程分别从给定的地址下载数据,用RandomAccessFile写到目标文件。实现思路是:

        1、获得连接的长度(即要下载的文件大小),除以设定的线程数,即得到每个线程要下载的大小。

        2、记录临时文件,文件中记录每个线程的编号(id),该线程要下载的起始位置、终止位置和当前位置(当前位置在首次下载时与起始位置相同)。

        3、启动具体执行下载任务的线程,并等待其结束。

        4、下载完成,删除临时文件。

    package com.why.download.test;
    
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.math.BigDecimal;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.UUID;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 
     * @author why
     *
     */
    public class DownLoad {
        //文件目录、文件名
        public String fileDir = "E:/MyDownLoad";
        public String fileName;
        //超时重连时间
        public long reconnectTime = 5;
        //线程数
        private int poolSize = 5;
        //每个线程的缓冲区大小
        public int bufferSize = 1024;
        //url地址
        private String urlLocation = null;
        
        public DownLoad(){}
        public DownLoad(String url){
            this.urlLocation = url;
        }
        public void downLoad(){
            if(this.urlLocation == null || "".equals(this.urlLocation))return;
            downLoad(this.urlLocation);
        }
        public void downLoad(String urlLocation){
            File file = null;
            File tempFile = null;
            CountDownLatch latch;
            URL url = null;
            ExecutorService pool = Executors.newCachedThreadPool();
            long contentLength = 0;
            long threadLength = 0;
            try {
                //如果未指定名称,则从url中获得下载的文件格式与名字
                if(fileName == null || "".equals(fileName)){
                    this.fileName = urlLocation.substring(urlLocation.lastIndexOf("/") + 1,
                            urlLocation.lastIndexOf("?") > 0 ? urlLocation.lastIndexOf("?")
                                    : urlLocation.length());
                    if ("".equalsIgnoreCase(this.fileName)) {
                        this.fileName = UUID.randomUUID().toString();
                    }
                }
                new File(fileDir).mkdirs();
                file = new File(fileDir + File.separator + fileName);
                tempFile = new File(fileDir + File.separator + fileName + "_temp");
                
                url = new URL(urlLocation);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                setHeader(conn);
                //得到content的长度
                contentLength = conn.getContentLength();
                
                System.out.println("total length=" + contentLength);
                
                //把context分为poolSize段,计算每段的长度。
    //            threadLength = contentLength / this.poolSize;
                BigDecimal b1 = new BigDecimal(Double.toString(contentLength));
                BigDecimal b2 = new BigDecimal(Double.toString(this.poolSize));
                threadLength = b1.divide(b2, 0, BigDecimal.ROUND_HALF_UP).longValue();
                
                if(file.exists() && tempFile.exists()){
                    //如果文件已存在,根据临时文件中记载的线程数量,继续上次的任务
                    latch = new CountDownLatch((int)tempFile.length()/28);
                    for(int i=0;i<tempFile.length()/28;i++){
                        pool.submit(new DownLoadTask(file, tempFile, url, i+1,latch,reconnectTime,bufferSize));
                    }
                }else{
                    //如果下载的目标文件不存在,则创建新文件
                    latch = new CountDownLatch(poolSize);
                    file.createNewFile();
                    tempFile.createNewFile();
                    DataOutputStream os = new DataOutputStream(new FileOutputStream(tempFile));
                    for(int i=0;i<this.poolSize;i++){
                        os.writeInt(i+1);
                        os.writeLong(i*threadLength);
                        if(i==this.poolSize-1){//最后一个线程的结束位置应为文件末端
                            os.writeLong(contentLength);
                        }else{
                            os.writeLong((i+1)*threadLength);
                        }
                        os.writeLong(i*threadLength);
                        pool.submit(new DownLoadTask(file, tempFile, url, i+1,latch,reconnectTime,bufferSize));
                    }
                    os.close();
                }
                //等待下载任务完成
                latch.await();
                //删除临时文件
                tempFile.delete();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{
                pool.shutdown(); 
            }
        }
        
        private void setHeader(URLConnection conn) {
            conn.setRequestProperty("User-Agent",
                            "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3");
            conn.setRequestProperty("Accept-Language", "en-us,en;q=0.7,zh-cn;q=0.3");
            conn.setRequestProperty("Accept-Encoding", "aa");
            conn.setRequestProperty("Accept-Charset","ISO-8859-1,utf-8;q=0.7,*;q=0.7");
            conn.setRequestProperty("Keep-Alive", "300");
            conn.setRequestProperty("Connection", "keep-alive");
            conn.setRequestProperty("If-Modified-Since", "Fri, 02 Jan 2009 17:00:05 GMT");
            conn.setRequestProperty("If-None-Match", ""1261d8-4290-df64d224"");
            conn.setRequestProperty("Cache-Control", "max-age=0");
            conn.setRequestProperty("Referer","http://www.skycn.com/soft/14857.html");
        }
    
        
        public String getFileDir() {
            return fileDir;
        }
        public void setFileDir(String fileDir) {
            this.fileDir = fileDir;
        }
        public String getFileName() {
            return fileName;
        }
        public void setFileName(String fileName) {
            this.fileName = fileName;
        }
        public long getReconnectTime() {
            return reconnectTime;
        }
        public void setReconnectTime(long reconnectTime) {
            this.reconnectTime = reconnectTime;
        }
        public int getPoolSize() {
            return poolSize;
        }
        public void setPoolSize(int poolSize) {
            this.poolSize = poolSize;
        }
        public int getBufferSize() {
            return bufferSize;
        }
        public void setBufferSize(int bufferSize) {
            this.bufferSize = bufferSize;
        }
        /**
         * @param args
         */
        public static void main(String[] args) {
            DownLoad dl = new DownLoad();
            dl.setFileDir("E:/MyDownLoad/music/");
            dl.setFileName("大笑江湖.mp3");
            dl.setPoolSize(20);
            long beginTime = System.currentTimeMillis();
            dl.downLoad("http://mh.163k.com/UploadFile/video/2010/12-13/201012131213448942190.mp3");
            long endTime = System.currentTimeMillis();
            BigDecimal b1 = new BigDecimal(endTime - beginTime);
            BigDecimal b2 = new BigDecimal(1000);
            double cost = b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP).doubleValue();
            System.out.println("Time cost:" + cost + "s");
        }
    
    }
    package com.why.download.test;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.concurrent.Callable;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 
     * @author why
     *
     */
    public class DownLoadTask implements Callable<String>{
        //超时重连时间
        private long reconnectTime = 5;
        //缓冲区大小
        private int bufferSize = 1024;
        
        private CountDownLatch latch;
        private RandomAccessFile file = null;
        private RandomAccessFile tempFile = null;
        private URL url = null;
        private int id;
        private long startPosition;
        private long endPosition;
        private long currentPosition ;
        
        public DownLoadTask(File file,File tempFile,URL url,int id,CountDownLatch latch,long reconnectTime,int bufferSize){
            try {
                this.file = new RandomAccessFile(file, "rw");
                this.tempFile = new RandomAccessFile(tempFile, "rw");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            this.url = url;
            this.id = id;
            this.latch = latch;
        }
        
        public String call(){
            
            try {
                tempFile.seek((id-1)*28);
                tempFile.readInt();
                this.startPosition = tempFile.readLong();
                this.endPosition = tempFile.readLong();
                this.currentPosition = tempFile.readLong();
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            System.out.println("Thread " + id + " begin!");
            
            HttpURLConnection conn = null;
            InputStream inputStream = null;
    
            while(true){
                try {
                    tempFile.seek(id*28 - 8);
                    // 打开URLConnection
                    conn = (HttpURLConnection) this.url.openConnection();
                    setHeader(conn);
                    // 设置连接超时时间为10000ms
                    conn.setConnectTimeout(10000);
                    // 设置读取数据超时时间为10000ms
                    conn.setReadTimeout(10000);
    
                    if (currentPosition < endPosition) {
                        // 设置下载数据的起止区间
                        conn.setRequestProperty("Range", "bytes=" + currentPosition + "-" + endPosition);
                        
                        System.out.println("Thread " + id + " startPosition=" + startPosition 
                                + ",endPosition=" + endPosition + ",currentPosition=" + currentPosition);
    
                        file.seek(currentPosition);
    
                        // 判断http status是否为HTTP/1.1 206 Partial Content或者200 OK
                        // 如果不是以上两种状态,把status改为STATUS_HTTPSTATUS_ERROR
                        if (conn.getResponseCode() != HttpURLConnection.HTTP_OK
                                && conn.getResponseCode() != HttpURLConnection.HTTP_PARTIAL) {
                            System.out.println("Thread " + id + ": code = " + conn.getResponseCode() + ", status = " + conn.getResponseMessage());
                            file.close();
                            conn.disconnect();
                            System.out.println("Thread " + id + " finished.");
                            break;
                        }
    
                        inputStream = conn.getInputStream();
                        int len = 0;
                        byte[] b = new byte[bufferSize];
                        while ((len = inputStream.read(b)) != -1) {
                            file.write(b, 0, len);
    
                            currentPosition += len;
                            // set tempFile now position
                            tempFile.seek(id*28 - 8);
                            tempFile.writeLong(currentPosition);
                        }
    
                        file.close();
                        tempFile.close();
                        inputStream.close();
                        conn.disconnect();
                    }
    
                    System.out.println("Thread " + id + " finished.");
                    break;
                } catch (IOException e) {
                    try {
                        TimeUnit.SECONDS.sleep(getReconnectTime());
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                    continue;
                }
            }
            latch.countDown();
            return "finish";
        }
        
        private void setHeader(URLConnection conn) {
            conn.setRequestProperty("User-Agent",
                            "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3");
            conn.setRequestProperty("Accept-Language", "en-us,en;q=0.7,zh-cn;q=0.3");
            conn.setRequestProperty("Accept-Encoding", "aa");
            conn.setRequestProperty("Accept-Charset","ISO-8859-1,utf-8;q=0.7,*;q=0.7");
            conn.setRequestProperty("Keep-Alive", "300");
            conn.setRequestProperty("Connection", "keep-alive");
            conn.setRequestProperty("If-Modified-Since", "Fri, 02 Jan 2009 17:00:05 GMT");
            conn.setRequestProperty("If-None-Match", ""1261d8-4290-df64d224"");
            conn.setRequestProperty("Cache-Control", "max-age=0");
            conn.setRequestProperty("Referer","http://www.skycn.com/soft/14857.html");
        }
    
        public long getReconnectTime() {
            return reconnectTime;
        }
    
        public void setReconnectTime(long reconnectTime) {
            this.reconnectTime = reconnectTime;
        }
    
        public int getBufferSize() {
            return bufferSize;
        }
    
        public void setBufferSize(int bufferSize) {
            this.bufferSize = bufferSize;
        }
    
        
    }
  • 相关阅读:
    初识Tensorboard
    sql优化的几种方法
    nginx+ffmpeg+jwplayer
    jwplayer播放器
    详解spring 每个jar的作用
    RabbitMQ安装
    Migration 使用方法
    VisualSVN server 启用日志编辑
    nodejs prefix(全局)和cache(缓存)windows下设置
    python3 eval字符串str 转字典dict
  • 原文地址:https://www.cnblogs.com/lbangel/p/3222946.html
Copyright © 2020-2023  润新知