栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 移动开发 > Android

PC版与Android手机版带断点续传的多线程下载

Android 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

PC版与Android手机版带断点续传的多线程下载

一、多线程下载

        多线程下载就是抢占服务器资源

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

      1、设置开启线程数,发送http请求到下载地址,获取下载文件的总长度
          然后创建一个长度一致的临时文件,避免下载到一半存储空间不够了,并计算每个线程下载多少数据      
       2、计算每个线程下载数据的开始和结束位置
          再次发送请求,用 Range 头请求开始位置和结束位置的数据
       3、将下载到的数据,存放至临时文件中
       4、带断点续传的多线程下载

          定义一个int变量,记录每条线程下载的数据总长度,然后加上该线程的下载开始位置,得到的结果就是下次下载时,该线程的开始位置,把得到的结果存入缓存文件,当文件下载完成,删除临时进度文件。

 public class MultiDownload {
    static int ThreadCount = ;
    static int finishedThread = ;
    //确定下载地址
    static String filename = "EditPlus.exe";
    static String path = "http://...:/"+filename;
    public static void main(String[] args) {
      //、发送get请求,去获得下载文件的长度
     try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout();
conn.setReadTimeout();
if (conn.getResponseCode()==) {
  //如果请求成功,拿到所请求资源文件的长度
  int length = conn.getContentLength();
  //、生成一个与原文件同样的大小的临时文件,以免下载一半存储空间不够了
  File file = new File(filename);//演示,所以将保存的文件目录放在工程的同目录
  //使用RandomAccessFile 生成临时文件,可以用指针定位文件的任意位置,
  //而且能够实时写到硬件底层设备,略过缓存,这对下载文件是突然断电等意外是有好处的
  RandomAccessFile raf = new RandomAccessFile(file, "rwd");//rwd, 实时写到底层设备
  //设置临时文件的大小
  raf.setLength(length);
  raf.close();
  //、计算出每个线程应该下载多少个字节
  int size = length/ThreadCount;//如果有余数,负责最后一部分的线程负责下砸
  //开启多线程
  for (int threadId = ; threadId < ThreadCount; threadId++) {
    //计算每个线程下载的开始位置和结束位置
    int startIndex = threadId*size; // 开始 = 线程id * size
    int endIndex = (threadId+)*size - ; //结束 = (线程id + )*size - 
    //如果是最后一个线程,那么结束位置写死为文件结束位置
    if (threadId == ThreadCount - ) {
      endIndex = length - ;
    }
    //System.out.println("线程"+threadId+"的下载区间是: "+startIndex+"----"+endIndex);
    new DownloadThread(startIndex,endIndex,threadId).start();
  }
}
     } catch (MalformedURLException e) {
e.printStackTrace();
     } catch (IOException e) {
e.printStackTrace();
     }
   }
 }
 class DownloadThread extends Thread{
   private int startIndex;
   private int endIndex;
   private int threadId;
   public DownloadThread(int startIndex, int endIndex, int threadId) {
     super();
     this.startIndex = startIndex;
     this.endIndex = endIndex;
     this.threadId = threadId;
   }
    public void run() {
      //每个线程再次发送http请求,下载自己对应的那部分数据
     try {
File progressFile = new File(threadId+".txt");
//判断进度文件是否存在,如果存在,则接着断点继续下载,如果不存在,则从头下载
if (progressFile.exists()) {
  FileInputStream fis = new FileInputStream(progressFile);
  BufferedReader br = new BufferedReader(new InputStreamReader(fis));
  //从进度文件中度取出上一次下载的总进度,然后与原本的开始进度相加,得到新的开始进度
  startIndex += Integer.parseInt(br.readLine());
  fis.close();
}
System.out.println("线程"+threadId+"的下载区间是:"+startIndex+"----"+endIndex);
//、每个线程发送http请求自己的数据
URL url = new URL(MultiDownload.path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout();
conn.setReadTimeout();
//设置本次http请求所请求的数据的区间
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
//请求部分数据,响应码是
if (conn.getResponseCode()==) {
  //此时,流里只有ThreadCount分之一的原文件数据
  InputStream is = conn.getInputStream();
  byte[] b = new byte[];
  int len = ;
  int total = ;//total 用于保存断点续传的断点
  //拿到临时文件的输出流
  File file = new File(MultiDownload.filename);
  RandomAccessFile raf = new RandomAccessFile(file, "rwd");
  //把文件的写入位置移动至 startIndex
  raf.seek(startIndex);
  while ((len = is.read(b))!=-) {
    //每次读取流里数据之后,同步把数据写入临时文件
    raf.write(b, , len);
    total += len;
    //System.out.println("线程" + threadId + "下载了" + total);
    //生成一个一个专门用来记录下载进度的临时文件
    RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd");
    progressRaf.write((total+"").getBytes());
    progressRaf.close();
  }
  System.out.println("线程"+threadId+"下载完了---------------------");
  raf.close();
  //当所有的线程下载完之后,将进度文件删除
  MultiDownload.finishedThread++;
  synchronized (MultiDownload.path) {//所有线程使用同一个锁
    if (MultiDownload.finishedThread==MultiDownload.ThreadCount) {
      for (int i = ; i < MultiDownload.ThreadCount; i++) {
 File f = new File(i+".txt");
 f.delete();
      }
      MultiDownload.finishedThread=;
    }
  }
}  
     } catch (Exception e) {
e.printStackTrace();
     }
   }
 }

二、Android手机版带断点续传的多线程下载

     Android手机版的带断点续传的多线程下载逻辑与PC版的几乎一样,只不过在Android手机中耗时操作不能放在主线程,网络下载属于耗时操作,所以多线程下载要在Android中开启一个子线程执行。并使用消息队列机制刷新文本进度条。

public class MainActivity extends Activity {
  static int ThreadCount = ;
  static int FinishedThread = ;
  int currentProgess;
  static String Filename = "QQPlayer.exe";
  static String Path = "http://...:/"+Filename;
  static MainActivity ma;
  static ProgressBar pb;
  static TextView tv;
  static Handler handler = new Handler(){
    public void handleMessage(android.os.Message msg){
      tv.setText((long)pb.getProgress()* /pb.getMax() +"%");
    };
  };
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ma = this;
    pb = (ProgressBar) findViewById(R.id.pb);
    tv = (TextView) findViewById(R.id.tv);
  }
  public void download(View v){
    Thread t = new Thread(){
      public void run() {
 //发送http请求获取文件的长度,创建临时文件
 try {
   URL url= new URL(Path);
   HttpURLConnection conn = (HttpURLConnection) url.openConnection();
   conn.setRequestMethod("GET");
   conn.setConnectTimeout();
   conn.setReadTimeout();
   if (conn.getResponseCode()==) {
     int length = conn.getContentLength();
     //设置进度条的最大值就是原文件的总长度
     pb.setMax(length);
     //生成一个与原文件相同大小的临时文件
     File file = new File(Environment.getExternalStorageDirectory(),Filename);
     RandomAccessFile raf = new RandomAccessFile(file, "rwd");
     raf.setLength(length);
     raf.close();
     //计算每个线程需要下载的数据大小
     int size = length/ThreadCount;
     //开启多线程
     for (int threadId = ; threadId < ThreadCount; threadId++) {
int startIndex = threadId*size;
int endIndex = (threadId + )*size - ;
if (threadId==ThreadCount - ) {
  endIndex = length - ;
}
new DownloadThread(startIndex, endIndex, threadId).start();
     }
   }
 } catch (Exception e) {
   e.printStackTrace();
 }
      }
    };
    t.start();
  }
  class DownloadThread extends Thread{
    private int startIndex;
    private int endIndex;
    private int threadId;
    public DownloadThread(int startIndex, int endIndex, int threadId) {
      super();
      this.startIndex = startIndex;
      this.endIndex = endIndex;
      this.threadId = threadId;
    }
    public void run() {
      // 每个线程发送http请求自己的数据
      try{
 //先判断是不是断点续传
 File progessFile = new File(Environment.getExternalStorageDirectory(),threadId+".txt");
 if (progessFile.exists()) {
   FileReader fr = new FileReader(progessFile);
   BufferedReader br = new BufferedReader(fr);
   int lastProgess = Integer.parseInt(br.readLine());
   startIndex += lastProgess;
   //把上次下载的进度显示至进度条
   currentProgess +=lastProgess;
   pb.setProgress(currentProgess);
   //发消息,让主线程刷新文本进度
   handler.sendEmptyMessage();
   br.close();
   fr.close();
 }
 URL url = new URL(Path);
 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 conn.setRequestMethod("GET");
 conn.setConnectTimeout();
 conn.setReadTimeout();
 conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
 if (conn.getResponseCode()==) {
   InputStream is = conn.getInputStream();
   byte[] buffer = new byte[];
   int len = ;
   int total = ;
   File file = new File(Environment.getExternalStorageDirectory(),Filename);
   RandomAccessFile raf = new RandomAccessFile(file, "rwd");
   raf.seek(startIndex);
   while ((len = is.read(buffer))!= -) {
     raf.write(buffer, , len);
     total += len;
     //每次读取流里数据之后,把本次读取的数据的长度显示至进度条
     currentProgess += len;
     pb.setProgress(currentProgess);
     //发消息,让主线程刷新文本进度
     handler.sendEmptyMessage();
     //生成临时文件保存下载进度,用于断点续传,在所有线程现在完毕后删除临时文件
     RandomAccessFile progressRaf = new RandomAccessFile(progessFile, "rwd");
     progressRaf.write((total+"").getBytes());
     progressRaf.close();
   }
   raf.close();
   System.out.println("线程"+threadId+"下载完了");
   //当所有线程都下在完了之后,删除临时进度文件
   FinishedThread++;
   synchronized (Path) {
     if (FinishedThread==ThreadCount) {
for (int i = ; i < ThreadCount; i++) {
  File f = new File(Environment.getExternalStorageDirectory(),i+".txt");
  f.delete();
}
FinishedThread=;
     }
   }
 }
      } catch (Exception e) {
 e.printStackTrace();
      }
    }
  }
}

以上内容是小编跟大家分享的PC版与Android手机版带断点续传的多线程下载,希望大家喜欢。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/164152.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号