zoukankan      html  css  js  c++  java
  • Android 基于Http的多线程下载的实现

    a、对于网络上的一个资源,首先发送一个请求,从返回的Content-Length中回去需要下载文件的大小,然后根据文件大小创建一个文件。

    this.fileSize = conn.getContentLength();// 根据响应获取文件大小
    File dir = new File(dirStr);
    this.localFile = new File(dir, filename);
    RandomAccessFile raf = new RandomAccessFile(this.localFile, "rw");
    raf.setLength(fileSize);
    raf.close();

    b、根据线程数和文件大小,为每个线程分配下载的字节区间,然后每个线程向服务器发送请求,获取这段字节区间的文件内容。

    conn.setRequestProperty("Range", "bytes=" + startPos + "-"
                            + endPos);// 设置获取实体数据的范围

    c、利用RandomAccessFile的seek方法,多线程同时往一个文件中写入字节。

    raf.seek(startPos);
    while ((len = is.read(buf)) != -1)
    {
        raf.write(buf, 0, len);
    }

    分析完了原理就很简单了,我封装了一个类,利用这个类的实例进行下载,所需参数:下载资源的URI, 本地文件路径,线程的数量。

    package com.zhy.mutilthread_download;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class MultipartThreadDownloador
    {
    
        /**
         * 需要下载资源的地址
         */
        private String urlStr;
        /**
         * 下载的文件
         */
        private File localFile;
        /**
         * 需要下载文件的存放的本地文件夹路径
         */
        private String dirStr;
        /**
         * 存储到本地的文件名
         */
        private String filename;
    
        /**
         * 开启的线程数量
         */
        private int threadCount;
        /**
         * 下载文件的大小
         */
        private long fileSize;
    
        public MultipartThreadDownloador(String urlStr, String dirStr,
                String filename, int threadCount)
        {
            this.urlStr = urlStr;
            this.dirStr = dirStr;
            this.filename = filename;
            this.threadCount = threadCount;
        }
    
        public void download() throws IOException
        {
            createFileByUrl();
    
            /**
             * 计算每个线程需要下载的数据长度
             */
            long block = fileSize % threadCount == 0 ? fileSize / threadCount
                    : fileSize / threadCount + 1;
    
            for (int i = 0; i < threadCount; i++)
            {
                long start = i * block;
                long end = start + block >= fileSize ? fileSize : start + block - 1;
    
                new DownloadThread(new URL(urlStr), localFile, start, end).start();
            }
    
        }
    
        /**
         * 根据资源的URL获取资源的大小,以及在本地创建文件
         */
        public void createFileByUrl() throws IOException
        {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(15 * 1000);
            conn.setRequestMethod("GET");
            conn.setRequestProperty(
                    "Accept",
                    "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
            conn.setRequestProperty("Accept-Language", "zh-CN");
            conn.setRequestProperty("Referer", urlStr);
            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty(
                    "User-Agent",
                    "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.connect();
    
            if (conn.getResponseCode() == 200)
            {
                this.fileSize = conn.getContentLength();// 根据响应获取文件大小
                if (fileSize <= 0)
                    throw new RuntimeException(
                            "the file that you download has a wrong size ... ");
                File dir = new File(dirStr);
                if (!dir.exists())
                    dir.mkdirs();
                this.localFile = new File(dir, filename);
                RandomAccessFile raf = new RandomAccessFile(this.localFile, "rw");
                raf.setLength(fileSize);
                raf.close();
    
                System.out.println("需要下载的文件大小为 :" + this.fileSize + " , 存储位置为: "
                        + dirStr + "/" + filename);
    
            } else
            {
                throw new RuntimeException("url that you conneted has error ...");
            }
        }
    
        private class DownloadThread extends Thread
        {
            /**
             * 下载文件的URI
             */
            private URL url;
            /**
             * 存的本地路径
             */
            private File localFile;
            /**
             * 是否结束
             */
            private boolean isFinish;
            /**
             * 开始的位置
             */
            private Long startPos;
            /**
             * 结束位置
             */
            private Long endPos;
    
            public DownloadThread(URL url, File savefile, Long startPos, Long endPos)
            {
                this.url = url;
                this.localFile = savefile;
                this.startPos = startPos;
                this.endPos = endPos;
            }
    
            @Override
            public void run()
            {
                System.out.println(Thread.currentThread().getName() + "开始下载...");
                try
                {
                    HttpURLConnection conn = (HttpURLConnection) url
                            .openConnection();
                    conn.setConnectTimeout(15 * 1000);
                    conn.setRequestMethod("GET");
                    conn.setRequestProperty(
                            "Accept",
                            "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
                    conn.setRequestProperty("Accept-Language", "zh-CN");
                    conn.setRequestProperty("Referer", url.toString());
                    conn.setRequestProperty("Charset", "UTF-8");
                    conn.setRequestProperty("Range", "bytes=" + startPos + "-"
                            + endPos);// 设置获取实体数据的范围
    
                    conn.setRequestProperty(
                            "User-Agent",
                            "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
                    conn.setRequestProperty("Connection", "Keep-Alive");
                    conn.connect();
    
                    /**
                     * 代表服务器已经成功处理了部分GET请求
                     */
                    if (conn.getResponseCode() == 206)
                    {
                        InputStream is = conn.getInputStream();
                        int len = 0;
                        byte[] buf = new byte[1024];
    
                        RandomAccessFile raf = new RandomAccessFile(localFile,
                                "rwd");
                        raf.seek(startPos);
                        while ((len = is.read(buf)) != -1)
                        {
                            raf.write(buf, 0, len);
                        }
                        raf.close();
                        is.close();
                        System.out.println(Thread.currentThread().getName()
                                + "完成下载  : " + startPos + " -- " + endPos);
                        this.isFinish = true;
                    } else
                    {
                        throw new RuntimeException(
                                "url that you conneted has error ...");
                    }
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
    
        }
    
        
    
    }

    createFileByUrl方法,就是我们上述的原理的步骤1,得到文件大小和创建本地文件。我在程序使用了一个内部类DownloadThread继承Thread,专门负责下载。download()方法,根据线程数量和文件大小计算每个线程需要下载的字节区间,然后开启线程去下载。

    服务器端:我就扔了几个文件在Tomcat根目录做实验,下面是测试代码:

    package com.zhy.mutilthread_download;
    
    import java.io.IOException;
    
    public class Test
    {
    
        public static void main(String[] args)
        {
            try
            {
                new MultipartThreadDownloador("http://localhost:8080/nexus.zip",
                        "f:/backup/nexus", "nexus.zip", 2).download();
            } catch (IOException e)
            {
                e.printStackTrace();
            }
    
        }
    }

    输出结果:

    需要下载的文件大小为 :31143237 , 存储位置为: f:/backup/nexus/nexus.zip
    Thread-1开始下载...
    Thread-2开始下载...
    Thread-3开始下载...
    Thread-4开始下载...
    Thread-4完成下载  : 23357430 -- 31143237
    Thread-2完成下载  : 7785810 -- 15571619
    Thread-1完成下载  : 0 -- 7785809
    Thread-3完成下载  : 15571620 -- 23357429

    截图:

    源码点击下载

  • 相关阅读:
    chrome浏览器中安装以及使用Elasticsearch head 插件
    windows10 升级并安装配置 jmeter5.3
    linux下部署Elasticsearch6.8.1版本的集群
    【Rollo的Python之路】Python 爬虫系统学习 (八) logging模块的使用
    【Rollo的Python之路】Python 爬虫系统学习 (七) Scrapy初识
    【Rollo的Python之路】Python 爬虫系统学习 (六) Selenium 模拟登录
    【Rollo的Python之路】Python 爬虫系统学习 (五) Selenium
    【Rollo的Python之路】Python 爬虫系统学习 (四) XPath学习
    【Rollo的Python之路】Python 爬虫系统学习 (三)
    【Rollo的Python之路】Python sys argv[] 函数用法笔记
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/7052732.html
Copyright © 2011-2022 走看看