Java之多线程断点下载的实现

Java之多线程断点下载的实现

大家好,又见面了,我是全栈君。

RandomAccessFile类:
此类的实例支持对随机訪问文件的读取和写入。随机訪问文件的行为相似存储在文件系统中的一个大型 byte 数组。

存在指向该隐含数组。光标或索引,称为文件指针。输入操作从文件指针開始读取字节。并随着对字节的读取而前移此文件指针。

假设随机訪问文件以读取/写入模式创建,则输出操作也可用。输出操作从文件指针開始写入字节。并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针能够通过 getFilePointer 方法读取。并通过 seek 方法设置。

以下有RandomAccessFile实现安卓下的断点下载的demo。

server端能够用tomcat模拟。将被下载的測试文件放入webApp/ROOT文件夹下就可以。
先给出java借助HttpURLConnection类实现的多线程下载代码:

public class MultiThread {
    private static int threadCount = 3;
    private static long blockSize;
    private static int runningThreadCount;
    public static void main(String[] args) throws Exception {
        String path = "http://10.0.67.172/test.exe";
        URL url = new URL(path); 
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);//超时时间
        int code = conn.getResponseCode();
        System.out.println(code);

        if(code / 100 == 2){
            int size = conn.getContentLength();//获取资源文件的长度
            System.out.println("请求资源大小:" + size);
            blockSize = size / threadCount;//将资源文件分为多少块。没一块的大小

            runningThreadCount = threadCount;
            long startIndex = 0;
            long endIndex = 0;
            //开启若干个子线程去实现多线程的下载
            for(int i = 0; i < threadCount; i++){
                startIndex = i * blockSize;
                endIndex = (i + 1) * blockSize - 1;
                if(i == threadCount-1){
                    endIndex = size - 1;
                }
                System.out.println("开启线程:" + i + ";" + "開始位置:" + startIndex + ":" + "结束位置:" + endIndex);
                new DownThread(path, startIndex, endIndex, i).start();
            }
        }
    }

    private static class DownThread extends Thread{
        private String path;
        private long startIndex;
        private long endIndex;
        private int threadId;

        public DownThread(String path, long startIndex, long endIndex, int threadId) {
            super();
            this.path = path;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.threadId = threadId;
        }

        @Override
        public void run() {
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setReadTimeout(5000);
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);//设置server上的文件的读取位置

                int code = conn.getResponseCode();
                if(code / 100 == 2){
                    InputStream is = conn.getInputStream();
                    File file = new File("temp.exe");
                    RandomAccessFile raf = new RandomAccessFile(file, "rw");
                    raf.seek(startIndex);
                    System.out.println("第" + threadId + "个文件的開始位置:" + String.valueOf(startIndex));
                    int len = 0;
                    byte[] buffer = new byte[1024];
                    while ((len = is.read(buffer)) != -1){
                        raf.write(buffer, 0, len);//写文件
                    }
                    raf.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

断点下载的原理就是将上次文件下载的位置保存为暂时文件,当全然完成下载时再删除。

public class MultiThread {
    private static int threadCount = 3;
    private static long blockSize;
    private static int runningThreadCount;
    public static void main(String[] args) throws Exception {
        String path = "http://10.0.67.172/test.rar";
        URL url = new URL(path); 
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);//超时时间
        int code = conn.getResponseCode();
        System.out.println(code);

        if(code / 100 == 2){
            int size = conn.getContentLength();//获取资源文件的长度
            System.out.println("请求资源大小:" + size);
            blockSize = size / threadCount;//将资源文件分为多少块,没一块的大小

            runningThreadCount = threadCount;
            long startIndex = 0;
            long endIndex = 0;
            for(int i = 0; i < threadCount; i++){
                startIndex = i * blockSize;
                endIndex = (i + 1) * blockSize - 1;
                if(i == threadCount-1){
                    endIndex = size - 1;
                }
                System.out.println("开启线程:" + i + ";" + "開始位置:" + startIndex + ":" + "结束位置:" + endIndex);
                new DownThread(path, startIndex, endIndex, i).start();
            }
        }
    }

    private static class DownThread extends Thread{
        private String path;
        private long startIndex;
        private long endIndex;
        private int threadId;

        public DownThread(String path, long startIndex, long endIndex, int threadId) {
            super();
            this.path = path;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.threadId = threadId;
        }

        @Override
        public void run() {
            int total = 0;
            try {
                File positionFile = new File(threadId + ".txt");

                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                //接着上次的文件继续下载
                if(positionFile.exists() && positionFile.length() > 0){
                    FileInputStream fis = new FileInputStream(positionFile);
                    BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
                    //获取当前线程上次下载的总大小是多少
                    String lasttotalstr = reader.readLine();
                    int lastTotal = Integer.valueOf(lasttotalstr);
                    System.out.println("上次线程下载的总大小:" + lastTotal);
                    startIndex += lastTotal;
                    total += lastTotal;
                    fis.close();
                }
                conn.setReadTimeout(5000);
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);//设置server上的文件的读取位置

                int code = conn.getResponseCode();
                if(code / 100 == 2){
                    InputStream is = conn.getInputStream();
                    File file = new File("temp.rar");
                    RandomAccessFile raf = new RandomAccessFile(file, "rw");
                    raf.seek(startIndex);
                    System.out.println("第" + threadId + "个文件的開始位置:" + String.valueOf(startIndex));
                    int len = 0;
                    byte[] buffer = new byte[1024];
                    while ((len = is.read(buffer)) != -1){
                        RandomAccessFile rf = new RandomAccessFile(positionFile, "rwd");
                        raf.write(buffer, 0, len);//写文件
                        total += len;
                        rf.write(String.valueOf(total).getBytes());
                        rf.close();
                    }
                    is.close();
                    raf.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                synchronized (DownThread.class) {
                    System.out.println("线程" + threadId + "完成下载了");
                    runningThreadCount--;
                    if (runningThreadCount < 1) {
                        System.out.println("全部的线程都工作完成了。删除暂时记录的文件");
                        for (int i = 0; i < threadCount; i++) {
                            File f = new File(i + ".txt");
                            System.out.println(f.delete());
                        }
                    }
                }
            }
        }
    }
}

执行结果截图:这里写图片描写叙述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/115621.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 批处理 %~dp0是什么意思「建议收藏」

    批处理 %~dp0是什么意思「建议收藏」cd/D%~dp0的意思如下:更改当前目录为批处理本身的目录比如你有个批处理a.bat在D:\qq文件夹下a.bat内容为cd/d%~dp0在这里cd/d%~dp0的意思就是cd/dd:\qq%0代表批处理本身d:\qq\a.bat~dp是变量扩充d既是扩充到分区号d:p就是扩充到路径\qqdp就是扩充到分区号路径d:\qq…

    2022年9月20日
    0
  • 世界各个地区WIFI 2.4G及5G信道一览表

    世界各个地区WIFI 2.4G及5G信道一览表世界各个地区WIFI2.4G及5G信道一览表

    2022年5月20日
    259
  • qtreewidget基本使用_qtreewidget列宽自适应

    qtreewidget基本使用_qtreewidget列宽自适应1、voidQTreeWidget::setHeaderLabels(constQStringList&labels)设置表头,QStringList有几项,表头就有多少列2、voidQHeaderView::setSortIndicatorShown(boolshow)Thispropertyholdswhetherthesortindicatoris

    2022年10月1日
    0
  • Pycharm社区版创建Flask项目详解「建议收藏」

    Pycharm社区版创建Flask项目详解「建议收藏」一、在原有工程上修改1、创建工程选择newproject创建工程输入项目名,选择配置好的虚拟环境项目创建好之后是一个空的项目,里面没有任何文件,下面我们来新建工程目录2、配置工程目录在工程根目录新建app.py文件在app.py中的代码如下:fromflaskimportFlask,render_templateapp=Flask(__name__)app.config[‘SECRET_KEY’]=’1456719640@qq.com’@app.rou

    2022年8月29日
    0
  • 404notfound软件下载_浏览器打开网址404

    404notfound软件下载_浏览器打开网址404当网上的那些修改程序池的方法,无法解决此问题时,可以尝试修改以下的参数:1.控制面板–&amp;gt;程序–&amp;gt;启用或关闭Windows功能–&amp;gt;InternetInformationServices–&amp;gt;Web管理工具–&amp;gt;子项全部勾选上.2.InternetInformationServices–&amp;gt;应用程序开发功能–&amp;gt;子项全部勾选上.重

    2025年6月7日
    0
  • Java编程语言简单常用的输入输出格式

    Java编程语言简单常用的输入输出格式Java语言和C语言的输入输出不同。C语言直接使用scanf()函数进行输入,使用printf()函数进行输出。而在Java中,所谓的函数有了一个新的名词,叫做方法。输入输出方法并不能想C语言那样可以默认直接使用。在使用前需要进行import进行类的导入,然后再进行方法的调用。具体实现,我们可以结合一段简单的代码来解释说明。源码如下:importjava.util.Scanner;publ…

    2022年7月8日
    18

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号