多线程下载

思路:

将文件分成多个部分,多个线程进行读取和写入,如3个线程下载1个大小为902byte的文件,第一个线程下载0-299字节,第二个线程下载300-599字节,第三个线程下载剩下的即600-901字节

需要用的知识点:

HTTP Range

HTTP1.1中,Range请求头可以指定下载的范围,从0开始计算

 

Range 请求头格式

Range: bytes=start-end
例如:

Range: bytes=0-39:第0个字节到第39个字节之间的数据

Range: bytes=40-100:第40个字节到最后一个字节的数据.

注意,这个表示[start,end],即是包含请求头的start及end字节的,所以,下一个请求,应该是上一个请求的[end+1, nextEnd]

             响应头

Content-Range

Content-Range: bytes 0-10/3103

这个表示,服务器响应了前(0-10)个字节的数据,该资源一共有(3103)个字节大小。

RandomAccessFile

 RandomAccessFile支持随机访问文件,通过seek()方法移动到指定位置进行读写

 

 

package io;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CountDownLatch;

public class MultiThreadDownloadFile
{
    public static void main(String []args) throws IOException, InterruptedException
    {
        URL url = new URL(
                "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1489511816166&di=c47b4abe7e4dad502b0672b8065c0fb6&imgtype=0&src=http%3A%2F%2Fimg.taopic."
                + "com%2Fuploads%2Fallimg%2F111130%2F573-1111301A01938.jpg");
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
        httpConn.setConnectTimeout(10000);
        httpConn.setReadTimeout(10000);
        //待下载的文件长度,用于计算各个线程的起始/结束位置
        int fileLen = httpConn.getContentLength();
        System.out.println("文件大小是:" +fileLen +" byte");
        
        //下载文件的线程个数
        int threadNums = 5;
        int oneLen = fileLen/threadNums;
        int []startPos = new int[threadNums];
        int []endPos = new int[threadNums];
        
        for(int i=0;i<threadNums;i++)
        {
            startPos[i] = i*oneLen;
            endPos[i] = (i+1)*oneLen-1;
        }
        //重新设置最后一个线程的endPos
        endPos[threadNums-1] = fileLen - 1;
    
        //下载文件本地保存路径/名称
        File downloadFile = new File("downloadFile.jpg");
        //用于线程同步
        CountDownLatch doneSignal = new CountDownLatch(threadNums);
        
        for(int i=0;i<threadNums;i++)
        {
            MultiDownloadThread mt = new MultiDownloadThread(url,doneSignal, downloadFile,startPos[i],endPos[i]);
            new Thread(mt).start();
        }
        
        //等待线程下载完成
        doneSignal.await();
        
        System.out.println("下载完成...文件下载大小是" + downloadFile.length() +" byte");
    }
}

 

 

package io;

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.util.concurrent.CountDownLatch;

public class MultiDownloadThread implements Runnable
{
    private URL url = null;
    private CountDownLatch doneSignal = null;
    private File file = null;
    private long startPos = 0;
    private long endPos = 0;
    private long curPos = 0;// 用于支持断点下载,未实现

    public MultiDownloadThread(URL url, CountDownLatch doneSignal, File file, long startPos, long endPos)
    {
        this.url = url;
        this.doneSignal = doneSignal;
        this.file = file;
        this.startPos = startPos;
        this.endPos = endPos;
    }

    @Override
    public void run()
    {
        System.out.println("启动了一个下载线程....");
        try
        {
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(1000);
            conn.setReadTimeout(1000);
            // 创建一个新的连接,设置下载的开始和结束位置
            conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
            // 如果请求部分资源 206 ok
            System.out.println("code=" + conn.getResponseCode()+", msg=" + conn.getResponseMessage());
            RandomAccessFile raf = new RandomAccessFile(file, "rw");
            raf.seek(startPos);
            InputStream os = conn.getInputStream();

            byte[] buff = new byte[1024];
            int len = 0;
            while ((len = os.read(buff)) != -1)
            {
                raf.write(buff, 0, len);
            }

            os.close();
            raf.close();

            doneSignal.countDown();

        } catch (FileNotFoundException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("线程下载完成....");
    }

}

 

参考:

http://emacsist.github.io/2015/12/29/Http-%E5%8D%8F%E8%AE%AE%E4%B8%AD%E7%9A%84Range%E8%AF%B7%E6%B1%82%E5%A4%B4%E4%BE%8B%E5%AD%90/

posted @ 2017-03-26 17:28  qqdcy  阅读(261)  评论(0)    收藏  举报