多线程分段下载文件

原理

先获取文件大小,然后分段分配任务给线程下载

在开始多线程下载前得先得知下载文件的大小,如果在之前的流程中并没有告知文件大小则可以使用HTTP请求方法 HEAD,这个请求方法类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头,在头部中可以找到字段content-length就是文件大小了。

得知文件长度后应分割需要下载的起止位置以便之后使用。
有具体位置后就可以启动多线程发送网络请求,在网络请求中使用HTTP协议的头部标志Range,这个标志的使用方法是Range:bytes=start-end。

实现

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: zlf
 * @Date: 2021/09/14/1:01
 * @Description:
 */
public class MutiThreadDownload {

    private int threadCount = 5; // 下载线程数

    private long blocksize; // 每线程下载区块大小

    private int runningThreadCount; // 正运行线程数


    public int getThreadCount() {
        return threadCount;
    }

    public void setThreadCount(int threadCount) {
        this.threadCount = threadCount;
    }

    public long getBlocksize() {
        return blocksize;
    }

    public void setBlocksize(long blocksize) {
        this.blocksize = blocksize;
    }

    public int getRunningThreadCount() {
        return runningThreadCount;
    }

    public void setRunningThreadCount(int runningThreadCount) {
        this.runningThreadCount = runningThreadCount;
    }

    /**
    * @Description: 多线程分段下载
    * @Param: [url, filePath]
    * @return: void
    * @Author: zlf
    * @Date: 2021/9/14
    */
    public void download(String url, String filePath) throws IOException {


        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();

        int code = conn.getResponseCode();

        if (code == 200) {
            long size = conn.getContentLength();// 文件大小

            blocksize = size / threadCount;

            // 1.创建RandomAccessFile文件(可以随机访问)

            RandomAccessFile raf = new RandomAccessFile(new File(filePath), "rw");

            raf.setLength(size);

            // 2.多线程下载对应区块

            runningThreadCount = threadCount;

            for (int i = 1; i <= threadCount; i++) {
                long startIndex = (i - 1) * blocksize;

                long endIndex = i * blocksize - 1;

                if (i == threadCount) {
                    endIndex = size - 1;

                }

                new DownloadThread(i, url, filePath,startIndex, endIndex).start();

            }

        }

        conn.disconnect();

    }


    /***
    * @Description: 分段下载的线程
    * @Param: 
    * @return: 
    * @Author: zlf
    * @Date: 2021/9/14
    */
    private class DownloadThread extends Thread {

        private int id;

        private String url;

        private long startIndex;

        private long endIndex;

        private String filePath;

        public DownloadThread(int id, String url, String filePath, long startIndex, long endIndex) {
            this.id = id;

            this.url = url;

            this.startIndex = startIndex;

            this.filePath = filePath;
            this.endIndex = endIndex;

        }

        @Override

        public void run() {
            try {
                HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();

                conn.setRequestMethod("GET");

                // 1.读取文件断点位置curIndex

                int curIndex = 0;

                File indexFile = new File("indexFile_"+id);

                if (indexFile.exists() && indexFile.length() > 0) {
                    FileInputStream fis = new FileInputStream(indexFile);

                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));

                    curIndex = Integer.valueOf(br.readLine());

                    startIndex += curIndex;

                    fis.close();

                }

                // 2.从startIndex位置下载文件, 并从startIndex位置写入raf文件

                conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);

                InputStream is = conn.getInputStream();

                RandomAccessFile raf = new RandomAccessFile(new File(filePath), "rw");

                raf.seek(startIndex);

                int len = 0;

                byte[] buffer = new byte[1024*1024];

                while ((len = is.read(buffer)) != -1) {
                    raf.write(buffer, 0, len);

                    // 更新断点位置

                    RandomAccessFile indexRaf = new RandomAccessFile(indexFile, "rwd");

                    curIndex += len;

                    indexRaf.write(String.valueOf(curIndex).getBytes());

                    indexRaf.close();

                }

                is.close();

                raf.close();

            } catch (Exception e) {
                e.printStackTrace();

            } finally {
                // 3.所有线程下载完,删除断点位置文件indexFile

                synchronized (MutiThreadDownload.class){
                    if (--runningThreadCount == 0) {
                        for (int i = 1; i <= threadCount; i++) new File("indexFile_"+i).delete();

                    }

                }

            }

        }

    }

    public static void main(String[] args) throws IOException {
        MutiThreadDownload mutiThreadDownload = new MutiThreadDownload();
        mutiThreadDownload.setRunningThreadCount(5);
        mutiThreadDownload.download("http://img.netbian.com/file/2020/0904/de2f77ed1090735b441ba5e4c2b460ca.jpg","D:/a.jpg");

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值