多线程下载

2023-10-31

原理:服务器cpu分配给每条线程的时间片是相同的,服务器带宽平均分配给每个线程,所以客户端开启的线程越多就能抢占到更多的服务器资源

java实现

public class NultiDownload {

    static String path = "http://192.168.43.157:8080/1.avi";

    static int ThreadCount = 3;

    @SuppressWarnings("resource")

    public static void main(String[] args) {

       try {

           URL url = new URL(path);

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

           conn.setRequestMethod("GET");

           conn.setConnectTimeout(5000);

           conn.setReadTimeout(5000);

           if (conn.getResponseCode() == 200) {

              // 拿到所请求的资源文件大小

              int length = conn.getContentLength();

              File file = new File("1.avi");

              // 生成临时文件

              RandomAccessFile raf = new RandomAccessFile(file, "rwd");

              // 设置临时文件的大小

              raf.setLength(length);

              raf.close();

              // 计算出每个线程应该下载多少字节

              int size = length / ThreadCount;

              for (int i = 0; i < ThreadCount; i++) {

                  // 计算出每个线程的开始位置和结束位置

                  int startIndex = i * size;

                  int endIndex = (i + 1) * size - 1;

                  if (i == ThreadCount - 1) {

                     endIndex = length - 1;

                  }

                  开启线程下载

                  new Mythread(startIndex, endIndex, i).start();

              }

           }

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

}

 

class Mythread extends Thread {

    int startIndex;

    int endIndex;

    int threadId;

 

    public Mythread(int startIndex, int endIndex, int threadId) {

       super();

       this.startIndex = startIndex;

       this.endIndex = endIndex;

       this.threadId = threadId;

    }

 

    @Override

    public void run() {

       // 再次发送http请求,下载原文件

       HttpURLConnection conn;

       try {

           URL url = new URL(NultiDownload.path);

           conn = (HttpURLConnection) url.openConnection();

           conn.setRequestMethod("GET");

           conn.setConnectTimeout(5000);

           conn.setReadTimeout(5000);

           // 设置本次http请求所请的数据的区间

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

           // 请求部分数据的响应码是206

           if (conn.getResponseCode() == 206) {

              InputStream is = conn.getInputStream();

              byte[] b = new byte[1024];

              int len = 0;

              int total = 0;

              File file = new File("1.avi");

              RandomAccessFile raf = new RandomAccessFile(file, "rwd");

              // 把文件的写入位置移动到startIndex

              raf.seek(startIndex);

              while ((len = is.read(b)) != -1) {

                  raf.write(b, 0, len);

                  total += len;

                  System.out.println("线程" + threadId + "下载了" + total);

              }

              raf.close();

           }

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

}

 

将已经下载好的保存到临时文件里去

// 生成一个专门用来记录进度的临时文件

File progreeFile = new File(threadId + ".txt");

 
   

RandomAccessFile progreeRaf = new RandomAccessFile(progreeFile, "rwd");

//每次读取流里面的数据之后,同步当前线程下载的总进度写入到临时文件中

progreeRaf.write((total+"").getBytes());

progreeRaf.close();

 

 

将临时文件读取出来,实现断点续传

File progreeFile = new File(threadId + ".txt");

//判断进度临时文件是否存在

if (progreeFile.exists()) {

    FileInputStream fis = new FileInputStream(progreeFile);

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

    //从进度临时文件中读取出上一次的总进度,然后与原本的开始进度位置相加,得到新的开始位置

    startIndex += Integer.parseInt(br.readLine());

}

 

 

实现下载完删除文件的功能

synchronized (NultiDownload.path){

NultiDownload.finishedThread++;

if (NultiDownload.finishedThread==NultiDownload.ThreadCount) {

    for(int i = 0;i<NultiDownload.ThreadCount;i++){

        File file2 = new File(i+".txt");

        file2.delete();

    }

NultiDownload.finishedThread = 0;}}加一个线程锁,防止一个线程下载完还没结束,另一个线程进入,引起重复删除

所有代码实现如下:

public class NultiDownload {

    static String path = "http://192.168.43.157:8080/1.avi";

    static int ThreadCount = 3;

    static int finishedThread = 0;

    @SuppressWarnings("resource")

    public static void main(String[] args) {

       try {

           URL url = new URL(path);

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

           conn.setRequestMethod("GET");

           conn.setConnectTimeout(5000);

           conn.setReadTimeout(5000);

           if (conn.getResponseCode() == 200) {

              // 拿到所请求的资源文件大小

              int length = conn.getContentLength();

              File file = new File("1.avi");

              // 生成临时文件

              RandomAccessFile raf = new RandomAccessFile(file, "rwd");

              // 设置临时文件的大小

              raf.setLength(length);

              raf.close();

              // 计算出每个线程应该下载多少字节

              int size = length / ThreadCount;

              for (int i = 0; i < ThreadCount; i++) {

                  // 计算出每个线程的开始位置和结束位置

                  int startIndex = i * size;

                  int endIndex = (i + 1) * size - 1;

                  if (i == ThreadCount - 1) {

                     endIndex = length - 1;

                  }

                  new Mythread(startIndex, endIndex, i).start();

              }

           }

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

}

 

class Mythread extends Thread {

    int startIndex;

    int endIndex;

    int threadId;

 

public Mythread(int startIndex, int endIndex, int threadId) {

    super();

    this.startIndex = startIndex;

    this.endIndex = endIndex;

    this.threadId = threadId;}

 

public void run() {

    // 再次发送http请求,下载原文件

    HttpURLConnection conn;

    try {

       File progreeFile = new File(threadId + ".txt");

       //判断进度临时文件是否存在

       if (progreeFile.exists()) {

           FileInputStream fis = new FileInputStream(progreeFile);

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

            //从进度临时文件中读取出上一次的总进度,然后与原本的开始进度位置相加,得到新的开始位置

           startIndex = Integer.parseInt(br.readLine());

           fis.close();

        }

       URL url = new URL(NultiDownload.path);

       conn = (HttpURLConnection) url.openConnection();

       conn.setRequestMethod("GET");

       conn.setConnectTimeout(5000);

       conn.setReadTimeout(5000);

       // 设置本次http请求所请的数据的区间

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

       // 请求部分数据的响应码是206

       if (conn.getResponseCode() == 206) {

           InputStream is = conn.getInputStream();

           byte[] b = new byte[1024];

           int len = 0;

           int total = 0;

           File file = new File("1.avi");

           RandomAccessFile raf = new RandomAccessFile(file, "rwd");

           // 把文件的写入位置移动到startIndex

           raf.seek(startIndex);

           while ((len = is.read(b)) != -1) {

              raf.write(b, 0, len);

              total += len;

              System.out.println("线程" + threadId + "下载了" + total);

              // 生成一个专门用来记录进度的临时文件

              RandomAccessFile progreeRaf = new RandomAccessFile(progreeFile, "rwd");

              //每次读取流里面的数据之后,同步当前线程下载的总进度写入到临时文件中

              progreeRaf.write((total+"").getBytes());

              progreeRaf.close(); 

           }

           raf.close();

          

synchronized (NultiDownload.path){

           NultiDownload.finishedThread++;

           if (NultiDownload.finishedThread==NultiDownload.ThreadCount) {

           for(int i = 0;i<NultiDownload.ThreadCount;i++){

              File file2 = new File(i+".txt");

              file2.delete();

           }

           NultiDownload.finishedThread = 0;

           }

           }

           }

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多线程下载 的相关文章

  • 有关 paddingStart 使用的冲突 lint 消息

    API 17 RTL 支持发布后 我将以下内容添加到我的清单中 android supportsRtl true 这导致 Lint 在我的视图中有 paddingLeft Right 的地方正确地向我发出这些警告 考虑添加 android
  • Android 布局不需要的填充

    所以我有这个布局文件 如下 正如您所看到的 没有填充或边距 dimen xml 文件也没有任何填充 边距 最后 我根本不以编程方式更改布局
  • Android STFP 库 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想在我的 Android 项目中使用 SFTP 安卓已经有了吗 SFTP 库 还是我必须实现它 I
  • Android Ble GATT_ERROR 133 经常使用三星设备

    我正在研究 BLE 应用程序 我已经使用 Nexus Moto Samsung LG 等不同设备进行了测试 我仅在三星设备中收到 GATT 错误 133 三星 A5 2016 尝试连接 10 次 但只连接了 2 或 3 次 请帮助我 Non
  • 居中复选框视图

    如果除了 或代替 复选框之外 您还对单选按钮感兴趣 请参阅this https stackoverflow com questions 16701806 centering views 2而是提问 尽管存在
  • Android 从键盘读取

    我的登录屏幕根本没有文本字段 当用户使用 RFID 扫描仪扫描他的 id 令牌时 我会得到一个 8 个字符长的字符串 其原理与使用键盘相同 只是更快 我希望我的登录活动在用户扫描其令牌时而不是之前执行 有一个聪明的方法来实现这个吗 我不能有
  • Mesibo 通话 UI 未更新

    我正在尝试更改 Mesibo Call UI 的配置 但它并没有改变 我尝试如下 MesiboCallConfig mesiboCallConfig new MesiboCallConfig mesiboCallConfig backgro
  • Android Accessibility 执行触摸操作

    我想知道是否可以使用 Android 辅助功能服务在屏幕上的位置执行触摸操作 例如 Bundle arguments new Bundle arguments putInt coord X X value arguments putInt
  • Android Studio:Android 设备监视器未显示我的设备

    我的真实设备是索尼 Xperia c6502安卓版本4 3 我确定我将其连接到我的计算机然后打开开发者选项 USB调试 on 在 SDK 管理器中 Google USB 驱动程序已安装 I downloaded Xperia Z Drive
  • 无论如何,要控制宋何时选择Android.bp,何时不选择?

    使用新的构建系统 即 Soong 安卓取代Android mk with Android bp 还有 Android Q 及以上版本 Soong将选择所有Android bp文件 无论所有文件都存在于何处 早些时候 对于 2 级和 3 级模
  • Android 导航回到 Activity;不要重新加载父级

    我有一个场景 我单击 ListFragment 并启动一个新的 Activity 如下所示 public void onListItemClick ListView l View v int position long id super o
  • 如何将 Google Now 搜索栏添加到我的应用程序中?

    谷歌刚刚将其搜索栏从 Google Now 引入到了 Play 商店应用程序中 如下面的 gif 所示 如何将这个操作栏搜索栏实现到我自己的应用程序中 我想要 style 汉堡动画 从工具栏按钮访问 麦克风按钮 对棒棒糖设备的连锁反应 我已
  • 将 firebase auth 与 google app engine 云端点集成

    有人可以指定 使用一些示例代码 如何验证谷歌云端点中的 firebase 令牌吗 最近提出的问题根本没有澄清 如何将 Firebase 身份验证与 Google 应用引擎端点集成 https stackoverflow com questi
  • Android 中带有透明背景的 ImageButton [重复]

    这个问题在这里已经有答案了 我已经按照这篇文章在android中制作ImageButton 安卓图像按钮 https stackoverflow com questions 2283444 android image button 图像出现
  • 如何在虚拟机 VirtualBox 上运行 Android-x86 4.2 iso?

    我想用Android x86测试和调试我的应用程序 我之前成功尝试过其他版本的Android x86 但是关于android x86 4 2有一个错误 所以我在这里问我的问题 因为它可能会发生在其他人身上 我安装了oracle VM vir
  • 如何在 Android 应用程序中使用多个 Graph API 获取 Facebook Notes 项目的评论?

    我想使用 Graph API 显示 Facebook 页面的注释项目以及这些评论和点赞 为此 我使用 Facebook SDK 中的 asyncFacebookRunner 步骤是这样的 调用 asyncFacebookRunner req
  • 在尝试使用 GPS 之前如何检查 GPS 是否已启用

    我有以下代码 但效果不好 因为有时 GPS 需要很长时间 我该如何执行以下操作 检查GPS是否启用 如果启用了 GPS 请使用 GPS 否则请使用网络提供商 如果 GPS 时间超过 30 秒 请使用网络 我可以使用时间或 Thread sl
  • 如何在 Android 中保存 Edittext 中的文本而不丢失文本的粗体、斜体等功能

    我想做的就是从 Edittext 中获取文本 该文本具有粗体和斜体等功能 并将其保存在文本文件中 但是当我读回并显示它时 这些功能丢失了 它们不显示 如何通过将文本保存在文本文件或任何文件中来保持丰富的功能 您可以使用Html toHtml
  • 获取当前图片在图库中显示的位置

    在我的应用程序中 我有一个图片库 但我想检测当前显示图像的位置 例如 当我启动我的活动时 位置是 0 但是当我在图库中滚动时 我想获取当前显示图像的位置 我尝试过 OnFocusChanged OnItemClicked 但只有当我单击图库
  • firebase中按范围查询

    我有一个食品价格范围滑块 根据滑块的最小值和最大值 我想显示此范围内的食品 滑块代码 multiSlider setOnThumbValueChangeListener new MultiSlider SimpleChangeListene

随机推荐

  • 计算机英语名词简释(轉載)

    计算机英语名词简释 一 著名公司及其商标名 Microsoft 有时缩略为MS 是全球最著名的软件商 美国软件巨头微软公司的名字 Microsoft其实是由两个英语单词组成 Micro意为 微小 Soft意为 软的 此处应为 Softwar
  • Spring boot 启用 Spring Data JPA Auditing(审计功能)

    一 Auditing功能简介 先贴上Spring Data JPA的官方文档 https docs spring io spring data jpa docs 2 0 9 RELEASE reference html 项目中每条数据在创建
  • 利用C语言 找出数组中的最大值和最小值

    include
  • cpuz北桥频率和内存频率_搞清楚时序与频率,买内存条就不会入坑

    很多时候购买者都会忽视在电脑中起到不可忽视作用的CPU以及硬盘 购买内存的时候 人们更加关注的也是它的价格以及所拥有的容量 而并非性能上的命门 频率 以及 时序 很多人表示好奇 究竟何为频率 所谓内存的频率 指的是在内存工作的过程中 以兆赫
  • 统计学中常见的分布汇总及相关概念

    常见分布 统计学中有很多常见的分布 在此对这些分布进行梳理 离散型随机变量分布 1 离散型均匀分布 若随机变量有n个不同值 具有相同概率 则我们称之为离散均匀分布 通常发生在我们不确定各种情况发生的机会 且认为每个机会都相等 例如 投掷骰子
  • datalab详细解析

    本博客参考了链接https blog csdn net qq 42792383 article details 86824293 特在此鸣谢 以下为datalab的部分解析 1 bitXor x y using only and Examp
  • Centos 部署Oracle 11g

    Centos 部署Oracle 11g 部署Oracle 11g 准备工作 服务器信息 oracle安装包 服务器准备oracle环境 安装Oracle 静默方式配置监听 以静默方式建立新库及实例 部署Oracle 11g 在SpringM
  • 传感器学习——蓝牙模块HC-05

    传感器学习 蓝牙模块HC 05 硬件连接VCC 接电源的正极 这里需要接 5V GND 接电源的负极 RXD 接收端 蓝牙模块接收从其他设备发来的数据 正常情况接其他设备的发送端TXD TXD 发送端 蓝牙模块发送数据给其他设备 正常情况接
  • 玩转Mysql系列 - 第7篇:玩转select条件查询,避免采坑

    这是Mysql系列第7篇 环境 mysql5 7 25 cmd命令中进行演示 电商中 我们想查看某个用户所有的订单 或者想查看某个用户在某个时间段内所有的订单 此时我们需要对订单表数据进行筛选 按照用户 时间进行过滤 得到我们期望的结果 此
  • mac电脑安装paste教程以及重新安装软件后不能使用解决方法

    问题背景 mac电脑安装paste教程以及重新安装软件后不能使用解决方法 mac电脑安装paste失败 安装好后还是无法使用 paste显示还是历史粘贴信息 导致无法使用 新 copy的内容也无法进入历史粘贴版里面 笔者电脑配置信息 Mac
  • PostgreSQL 锁表

    PostgreSQL 锁表解决 解决 一 碰到的问题 Navicat删除表是一直转圈 出现问题的原因 短时间多次对同一张表进行查询造成表死锁 解决思路 通过查询进程把锁住的pid查出来然后解锁 1 查询正在运行的进程 datname为数据库
  • sqlserver连接字符串_10分钟使用EF Core连接MSSQL数据库

    给DotNet加星标 提升 Net技能 转自 Ron liang cnblogs com viter p 10243577 html 前言 在 NET Core 2 2中Microsoft AspNetCore App 默认内置了Entit
  • 【UE】读写配置文件 ue读写ini文件

    UE读写ini文件 前言 1 新建C 文件 2 编写代码 2 1 头文件h文件 2 2 cpp文件 3 函数说明 3 1 写入ini 3 2 读取ini 4 打包后读取ini数据 4 1 打开Editor时的Game ini 4 2 打包后
  • 快速成长的秘诀|学会自我培养和培养他人

    快速成长总共三篇 分别是 完成自我认知升级 自我成长好方法 和 自我培养和培养他人 本篇是第三篇 篇幅较长 针对长文的阅读方式 依旧建议在 完成自我认知升级 中提到的阅读方式 在一个不被打扰的时间做好只字不差阅读 用批判性思维思考和理解其中
  • 银行从业中级系列课程之——内容概述

    课程初衷 由于在体制内工作 银行从业已经成了内部要求 本人连续两年重在参与了 终于决定为了奖励的400元战斗一下 同时提升下个人的银行从业水平 课程简介 本课程根据2021年课程大纲进行汇总 同时结合了233网校的课程内容进行了编写 力求能
  • linux编译mesa,编译安装 Mesa (OpenGL)

    最近因为工作需要 想看一下 OpenGL 的东西 就在笔记本上搭建了 OpenGL 的环境 解压后 运行 configure 根据提示安装必要的包 编译 安装 我安装到了 usr local private 下面 usr local pri
  • 佳威线管分级说明

    次次底级 keb sl刹车 次底 cgx sl刹车 lex sl变速 底级 无润滑 cex刹车 lex变速
  • Spark Task的各个动作时间来源以及Task Schedule Delay 问题排查

    背景 发现线上偶尔会出现一些Spark Task会显示Schedule Delay数分钟甚至十几分钟 所以要排查原因 Task 时间分析以及Schedule Delay计算公式 如图所示 时间轴上面的表示Driver 记录到的各个时间 时间
  • 结构体与共用体(联合体)

    参考 结构体与共用体 作者 一只青木呀 发布时间 2020 08 09 08 29 22 网址 https blog csdn net weixin 45309916 article details 107889394 参考 联合体 uni
  • 多线程下载

    原理 服务器cpu分配给每条线程的时间片是相同的 服务器带宽平均分配给每个线程 所以客户端开启的线程越多就能抢占到更多的服务器资源 用java实现 public class NultiDownload static String path