zoukankan      html  css  js  c++  java
  • java 多线程下载文件并实时计算下载百分比(断点续传)

    多线程下载文件

    多线程同时下载文件即:在同一时间内通过多个线程对同一个请求地址发起多个请求,将需要下载的数据分割成多个部分,同时下载,每个线程只负责下载其中的一部分,最后将每一个线程下载的部分组装起来即可。

    涉及的知识及问题

    • 请求的数据怎么拆分
    • 拆分完成后怎么下载
    • 如何计算实时下载量

    一.请求的数据怎么拆分

     1 int blockSize =fileLength/threadCount; //计算每个线程需要下的长度
     2     for(int i=0;i<threadCount;i++) {
     3         int startSize=i*blockSize; //当前线程需要下载的开始位置
     4         int endSize=(i+1)*blockSize-1;//当前线程需要下载的结束位置
     5         if(1+i==threadCount) { //最后一个线程的结尾赋值文件大小
     6             endSize=fileLength;
     7         }
     8         threadList[i]= new DownThread_1(filePath, fileUrl, "线程"+i, startSize, endSize);
     9         threadList[i].start();
    10     }
    11                 

    二.拆分完成后怎么下载

    try {
                URL url = new URL(urlPath);
                HttpURLConnection coon = (HttpURLConnection) url.openConnection();
                coon.setRequestProperty("range","bytes="+startSize+"-"+endSize); //设置获取下载资源的开始位置和结束位置
                coon.setConnectTimeout(5000);
                if(coon.getResponseCode()==206) {//响应码   因为上面设置了range 所有响应码是206不再是200
                    BufferedInputStream bi=new BufferedInputStream(coon.getInputStream());
                    RandomAccessFile raf=new RandomAccessFile(filePath, "rwd"); //断点续传的关键
                    raf.seek(startSize); //将写入点移动到当前线程写入开始位置
                    byte b[]=new byte[1024];
                    int len=0;
                    while ((len=bi.read(b))>-1) { //循环写入
                        raf.write(b, 0, len);
                        synchronized (DownUtile.class) {//此处涉及到变量同步
                            DownUtile.downLength=DownUtile.downLength+len; //计算当前下载了多少
                        }
                    }
                    raf.close();
                    bi.close();
                    System.out.println("thread"+threadName+"下载完成,开始位置"+startSize+",结束位置"+endSize);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

    三.如何计算实时下载量

        while ((len=bi.read(b))>-1) { //循环写入
               raf.write(b, 0, len);
               synchronized (DownUtile.class) {//此处涉及到变量同步
                    DownUtile.downLength=DownUtile.downLength+len; //计算当前下载了多少
               }
            }
    
        while(DownUtile.downOver) {
             Thread.sleep(500); //间隔0.5秒计算一下
             if(DownUtile.downLength==fileLength) {
                 DownUtile.downOver=false;
                 System.out.println("下载完成:100%");
             }else {
                  System.out.println("已经下载了:"+((int) (float)DownUtile.downLength / (float) fileLength * 100)+"%");
             }
        }

    上述方法中  用到了synchronized(类锁),为什么用到类锁,因为计算下载的参数在DownUtile类中,为了保证这个参数在多线程中同步,需保证在执行累加操作时线程安全。

     

    下面贴上全部的代码

     1 public class DownLoadUtile {
     2     public static String filePath="C:\Users\Administrator\Desktop\下载\ deme.exe"; //文件保存地址
     3     public static String fileUrl="http://123.6.39.120/dlied1.qq.com/lol/dltools/LOL_V4.1.2.3-V4.1.2.4_PATCH_0_tgod_signed.exe?mkey=5cfc8d87dddd9a57&f=5844&cip=221.221.188.162&proto=http";//文件地址
     4     public static int threadCount=5; //线程数量
     5     public static int fileLength=0; //文件大小
     6     public static Thread [] threadList=new Thread[threadCount];
     7     
     8     public DownLoadUtile() {
     9         
    10     }
    11     public DownLoadUtile(int threadCount) {//有参构造
    12         this.threadCount=threadCount;
    13     }
    14     
    15     public static void main(String[] args) throws Exception {
    16         URL url=new URL(fileUrl);
    17         HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //创建连接对象
    18         conn.setConnectTimeout(5000);//请求超时时间
    19         
    20         int code = conn.getResponseCode();
    21         System.out.println("服务器响应码"+code);
    22         if(code==200) {//响应正常
    23             fileLength=conn.getContentLength(); //获取文件大小
    24             File file = new File(filePath);
    25             if(!file.exists()) {
    26                 file.createNewFile();
    27             }
    28             RandomAccessFile raf = new RandomAccessFile(filePath, "rwd"); //断点续传的关键
    29             raf.setLength(fileLength);
    30             
    31             int blockSize =fileLength/threadCount; //计算每个线程需要下的长度
    32             for(int i=0;i<threadCount;i++) {
    33                 int startSize=i*blockSize; //当前线程需要下载的开始位置
    34                 int endSize=(i+1)*blockSize-1;//当前线程需要下载的结束位置
    35                 if(1+i==threadCount) { //最后一个线程的结尾赋值文件大小
    36                     endSize=fileLength;
    37                 }
    38                 threadList[i]= new DownThread_1(filePath, fileUrl, "线程"+i, startSize, endSize);
    39                 threadList[i].start();
    40             }
    41             
    42             while(DownUtile.downOver) {
    43                 Thread.sleep(500); //间隔0.5秒计算一下
    44                 if(DownUtile.downLength==fileLength) {
    45                     DownUtile.downOver=false;
    46                     System.out.println("下载完成:100%");
    47                 }else {
    48                     System.out.println("已经下载了:"+((int) (float)DownUtile.downLength / (float) fileLength * 100)+"%");
    49                 }
    50             }
    51             
    52         }else {
    53             System.out.println("服务器响应失败"+code);
    54         }
    55         
    56     }

    下载类

     1 public class DownThread_1 extends Thread{
     2 
     3     private String filePath;
     4     private String urlPath;
     5     private String threadName;
     6     private int startSize;
     7     private int endSize;
     8     
     9     public DownThread_1(String filePath,String urlPath,String threadName,int startSize,int endSize) {
    10         this.endSize=endSize;
    11         this.startSize=startSize;
    12         this.filePath=filePath;
    13         this.urlPath=urlPath;
    14         this.threadName=threadName;
    15     }
    16     @Override
    17     public void run() {
    18          try {
    19             URL url = new URL(urlPath);
    20             HttpURLConnection coon = (HttpURLConnection) url.openConnection();
    21             coon.setRequestProperty("range","bytes="+startSize+"-"+endSize); //设置获取下载资源的开始位置和结束位置
    22             coon.setConnectTimeout(5000);
    23             if(coon.getResponseCode()==206) {//响应码   因为上面设置了range 所有响应码是206不再是200
    24                 BufferedInputStream bi=new BufferedInputStream(coon.getInputStream());
    25                 RandomAccessFile raf=new RandomAccessFile(filePath, "rwd"); //断点续传的关键
    26                 raf.seek(startSize); //将写入点移动到当前线程写入开始位置
    27                 byte b[]=new byte[1024];
    28                 int len=0;
    29                 while ((len=bi.read(b))>-1) { //循环写入
    30                     raf.write(b, 0, len);
    31                     synchronized (DownUtile.class) {//此处涉及到变量同步
    32                         DownUtile.downLength=DownUtile.downLength+len; //计算当前下载了多少
    33                     }
    34                 }
    35                 raf.close();
    36                 bi.close();
    37                 System.out.println("thread"+threadName+"下载完成,开始位置"+startSize+",结束位置"+endSize);
    38             }
    39         } catch (MalformedURLException e) {
    40             e.printStackTrace();
    41         } catch (IOException e) {
    42             e.printStackTrace();
    43         }
    44     }
    45 
    46 }

    参数类

    1 public class DownUtile {
    2     
    3     public static  int downLength=0;//已经下载了多少
    4     
    5     public static boolean downOver=true; //下载是否完成
    6     
    7 }
  • 相关阅读:
    mysql语句删除重复数据,保留一条;查询所有重复数据;查询重复数据的一条,
    使用pycharm 运行python的django项目时报错“Quit the server with CTRL-BREAK.”
    检查python以及django是否安装配置成功
    使用Pycharm开发python下django框架项目生成的文件解释
    XML序列化与反序列化
    前台异步传过来的URL中获取token/获取string链接中的token
    字符串json转成json对象
    MySql查询语句中解决“该列没有包含在聚合函数或者groupby子句中”的相关问题方法
    图形验证码的生成(数字和英文大小写)和提交验证
    JQuery使用正则表达式验证手机号,邮箱,身份证(含有港澳台),网址
  • 原文地址:https://www.cnblogs.com/codebj/p/10994490.html
Copyright © 2011-2022 走看看