Java多線程文件分片下載實現

分片下載

所謂分片下載就是要利用多線程的優點,將要下載的文件一塊一塊的分配到各個線程中去下載,這樣就極大的提升了下載速度。java

技術難點

並不能說是什麼難點,只能說沒接觸過不知道罷了。git

一、如何請求才能拿到數據的特定部分,而非所有?github

能夠在HTTP請求頭中加入Range來標識數據的請求範圍/區間,從HTTP/1.1開始可用。多線程

基本用法:dom

Range: bytes=10-:取第10個字節及後全部數據。ide

Range: bytes=40-100:取第40個字節到第100個字節之間的數據。函數

這樣咱們就能拿到特定部分的數據了,斷點續傳也能夠用這個來實現。優化

PS:0爲開始點。this

二、分片後某線程下載時如何寫出?.net

思路1:等全部下載完成後進行統一彙總整理而後再一次性寫出。

這簡直是最笨的思路了,若是文件過大所有拉到內存中,豈不涼涼。

思路2:下載採用多線程,寫出時採起數據先後順序排隊寫出。

也就是說多線程下載,單線程輸出,某種程度解決了內存佔用問題,不過效率基本不理想。

思路3:要說仍是API香,老大哥Java給咱們提供了一個類叫作RandomAccessFile

這個類能夠進行隨機文件讀寫,其中有一個seek函數,能夠將指針指向任意位置,而後進行讀寫。什麼意思呢,舉個栗子:假如咱們開了30個線程,首先第一個下載完成的是線程X,它下載的數據範圍是4000-9000,那麼這時咱們調用seek函數將指針撥動到4000,而後調用它的write函數將byte寫出,這時4000以前都是NULL,4000以後就是咱們插入的數據。這樣就能夠實現多線程下載和本地寫入了。

具體實現

一個分片下載類,咱們須要建立多個對象來進行下載。

public class UnitDownloader implements Runnable {
    private int from;
    private int to;
    private File target;
    private String uri;
    private int id;

    public UnitDownloader(int from, int to, File target, String uri, int id) {
        this.from = from;
        this.to = to;
        this.target = target;
        this.uri = uri;
        this.id = id;
    }

    public int getFrom() {
        return from;
    }

    public int getTo() {
        return to;
    }

    @Override
    public void run() {
        //download and save data
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();
            connection.setRequestProperty("Range", "bytes=" + from + "-" + to);
            connection.connect();
            int totalSize = connection.getContentLength();
            InputStream inputStream = connection.getInputStream();
            RandomAccessFile randomAccessFile = new RandomAccessFile(target, "rw");
            randomAccessFile.seek(from);
            byte[] buffer = new byte[1024 * 1024];
            int readCount = inputStream.read(buffer, 0, buffer.length);
            while (readCount > 0) {
                totalSize -= readCount;
                System.out.println("分片:" + this.id + "的剩餘:" + totalSize);
                randomAccessFile.write(buffer, 0, readCount);
                readCount = inputStream.read(buffer, 0, buffer.length);
            }
            inputStream.close();
            randomAccessFile.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

分片下載管理器,主要就是拿到內容的總大小,將其分配給每個UnitDownloader。這裏的threadCount函數能夠再考慮優化一下。

public class MultipleThreadDownloadManager implements Runnable {
    private String uri;
    private File target;

    public MultipleThreadDownloadManager(String uri, File target) {
        this.target = target;
        this.uri = uri;
        if (target.exists() == false) {
            try {
                target.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 開始下載
     */
    public void start() {
        new Thread(this).start();
    }


    /**
     * 根據文件總大小計算線程數量
     *
     * @param totalSize
     * @return
     */
    public int threadCount(int totalSize) {
        if (totalSize < 30 * 2014 * 1024) {
            return 1;
        }
        return 30;
    }


    @Override
    public void run() {
        //獲取文件總大小
        int totalSize = 0;
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();
            connection.connect();
            int contentLength = connection.getContentLength();
            totalSize = contentLength;
        } catch (IOException e) {
            e.printStackTrace();
        }
        //將文件分片並分開下載
        int threadCount = threadCount(totalSize);
        int perThreadSize = totalSize / threadCount;//每個線程分到的任務下載量
        int id = 0;
        int from = 0, to = 0;
        while (totalSize > 0) {
            id++;
            //計算分片
            if (totalSize < perThreadSize) {
                from = 0;
                to = totalSize;
            } else {
                from = totalSize;
                to = from + perThreadSize;
            }
            //開始下載
            UnitDownloader downloader = new UnitDownloader(from, to, target, uri, id);
            new Thread(downloader).start();
        }
    }
}

參考文獻

一、https://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/

二、https://blog.csdn.net/lyt_7cs1dn9/article/details/75105266

相關文章
相關標籤/搜索