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)
上一篇 2022年2月1日 下午10:00
下一篇 2022年2月1日 下午10:00


相关推荐

  • 5分钟学习23种设计模式

    5分钟学习23种设计模式设计模式的分类总体来说设计模式分为三大类 创建型模式 共五种 工厂方法模式 抽象工厂模式 单例模式 建造者模式 原型模式 结构型模式 共七种 适配器模式 装饰器模式 代理模式 外观模式 桥接模式 组合模式 享元模式 行为型模式 共十一种 策略模式 模板方法模式 观察者模式 迭代子模式 责任链模式 命令模式 备忘录模式 状态模式 访问者模式 中介者模式 解释器模式

    2026年3月18日
    2
  • DirectX修复工具使用技巧之三——命令行与配置文件参数介绍

    DirectX修复工具使用技巧之三——命令行与配置文件参数介绍最后更新:2021-10-8DirectX修复工具自V1.0版起即支持通过settings.ini配置文件的方式设置程序配置信息,自V3.7版起支持命令行配置功能。由于命令行与配置文件均需要通过参数设定程序配置信息,本文档详细列出了命令行与配置文件的参数使用方式,供有需要的高级用户参考。命令行与配置文件中约有90%的命令是通用的,部分特殊命令只支持命令行的配置方式,本文件将分别介绍命令行参数与配置文件参数。命令行参数本程序支持命令行参数提示,用户可通过/?或者/h…

    2022年5月22日
    41
  • WiFi 的认证方法

    WiFi 的认证方法认证方法有两种:Opensystemauthentication与Sharedkeyauthentication。

    2022年7月11日
    42
  • Java中如何通过键盘输入一个数组

    Java中如何通过键盘输入一个数组有时候在编写Jave的时候需要键盘输入一个数组,本小白也是看了几篇博客后才知道了如何在自己的程序中进行键盘输入,废话不多说,直接上代码:第一种方法:(不限制输入数组的长度)System.out.println("请输入几个数并用逗号隔开:");Scannersc=newScanner(System.in);Stringstr=sc.next().toString();…

    2022年6月26日
    43
  • vue响应式原理的实现

    vue响应式原理的实现Vue最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的JavaScript对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接,不过理解其工作原理同样重要,这样你可以避开一些常见的问题。—-官方文档引言Vue的数据双向绑定,响应式原理,其实就是通过Object.defineProperty()结合发布者订阅者模式来实现的。Observer通过O…

    2022年5月1日
    43
  • JTAG与SWD接口引脚定义[通俗易懂]

    JTAG与SWD接口引脚定义[通俗易懂]看下载器的豁口或凸口,找引脚的起始位置

    2022年5月24日
    96

发表回复

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

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