多线程断点下载

一、断点下载:
1.使用HttpURLConnection实现断点下载。
    
①通过服务器资源路径来得到URL。
    ②通过URL得到HttpURLConnection对象,并且设置发送请求方式和请求参数等。
    ③得到资源文件的大小和线程个数,创建一个相同大小的RandomAccessFile文件。
    ④ 得到每个线程需要下载的区块大小,计算每个线程对应的下载的位置(startIndex~endIndex)。
    ⑤创建一个文件记录已经下载资源的总大小,当下次下载时,读取该文件的内容,即可从上次下载之后的位置开始下载。
    ⑥创建并开启线程。
    ⑦全部线程下载完毕后,删除记录位置的文件。

2.断点下载的细节:
   ①RandomAccessFile的mode有"r","rw","rws","rwd"四种,其中"r","rw"是先将更新写入存储设备的缓冲区,若系统崩溃会丢失数据。而"rwd","rws",是直接将更新同步到存储设备,系统崩溃时也不会丢失数据。"rws"将内容与元数据的每个更新都同步到存储设备,"rwd"只将内容的更新同步到存储设备。所以一般使用"rwd"。
  ②byte [] buffer的大小设置大一点,可以加快下载速度。
  ③当全部线程下载完成后,才能删掉所有的保存位置的文件。
 
 3.断点下载的功能实现代码:
public class DownUtil {
 /**
  * 下载资源的路径
  */
 private String sourcePath;
 /**
  * 下载资源的文件保存路径
  */
 private String filePath;
 /**
  * 线程的个数
  */
 private int threadCount;
 /**
  * 每个线程下载的区块大小
  */
 private long blockSize;
 /**
  * 正在运行的线程个数
  */
 private int runningThreadCount;
 public DownUtil(String sourcePath, String filePath, int threadCount) {
  this.sourcePath = sourcePath;
  this.filePath = filePath;
  this.threadCount = threadCount;
 }
 /**
  * 下载
  *
  * @throws Exception
  */
 public void downLoad() throws Exception {
  URL url = new URL(sourcePath);
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  conn.setRequestMethod("GET");                          // 发送GET请求
  conn.setConnectTimeout(5 * 1000);                      // 设置连接超时时长
  int code = conn.getResponseCode();                     // 获得服务器的响应码
  if (code == 200) {
   int size = conn.getContentLength();                // 得到资源总大小
      // 创建一个随机访问的可读写文件
   RandomAccessFile file = new RandomAccessFile(filePath, "rw");
   file.setLength(size);                             // 设置文件大小
   blockSize = size / threadCount;
           
   runningThreadCount=threadCount;                  
   // 计算每个线程的下载位置
   for (int i = 1; i <= threadCount; i++) {
    long startIndex = (i - 1) * blockSize;        // 线程下载的开始位置
    long endIndex = i * blockSize - 1;            // 线程下载的结束位置
    if (i == threadCount) {
     endIndex = size - 1;
    }
    // 创建并开启线程
    new DownThread(startIndex, endIndex, i).start();
   }
  }
  // 释放连接
  conn.disconnect();
 }
 /**
  * 线程下载类
  *
  */
 private class DownThread extends Thread {
  /**
   * 下载的开始位置
   */
  private long startIndex;
  /**
   * 下载的结束位置
   */
  private long endIndex;
  /**
   * 线程的ID
   */
  private int threadId;
  public DownThread(long startIndex, long endIndex, int threadId) {
   this.startIndex = startIndex;
   this.endIndex = endIndex;
   this.threadId = threadId;
  }
  @Override
  public void run() {
   try {
    //已经下载的资源总大小
    int total = 0;
   //创建一个文件保存下载的位置
    File positionFile = new File( 
 Environment.getExternalStorageDirectory(), threadId 
+ ".txt"); 
  //判断该文件是否存在
    if (positionFile.exists() && positionFile.length() > 0) {
     FileInputStream fis = new FileInputStream(positionFile); // 得到该文件的输入流
     BufferedReader br = new BufferedReader( 
new InputStreamReader(fis)); 
     int lastTotal = Integer.valueOf(br.readLine()); // 上次下载的总大小
     startIndex += lastTotal;
     total += lastTotal;
     fis.close();
    }
    URL url = new URL(sourcePath);
    HttpURLConnection conn = (HttpURLConnection) url 
 .openConnection(); 
    conn.setRequestMethod("GET");   // 发送GET请求
    // 必须设置该请求参数
    conn.setRequestProperty("Range", "bytes=" + startIndex + "-" 
+ endIndex); 
    conn.setConnectTimeout(5 * 1000); // 设置连接超时时长
    int code = conn.getResponseCode(); // 获得服务器的响应码
    if (code == 206) {
     InputStream is = conn.getInputStream(); // 得到输入流
     RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
     // 从指定位置开始下载
     raf.seek(startIndex);
     int len = 0;
     // 缓存区设置越大,下载速度越快
     byte[] buffer = new byte[1024];
     while ((len = is.read(buffer)) != -1) {
      raf.write(buffer, 0, len);
   //创建一个同步更新到存储设备的RandomAccessFile对象
      RandomAccessFile file = new RandomAccessFile( 
positionFile, "rwd"); 
      total+=len;
   //必须将total转化为字节
      file.write(String.valueOf(total).getBytes());
      file.close();
     }
     raf.close();
     is.close();
    }
   } catch (Exception e) {
    e.printStackTrace();
   } finally {
    System.out.println("线程" + threadId + "下载完毕了!");
  //当全部线程执行完毕后,才能删除记录位置的文件,所有使用synchronized。
    synchronized(DownUtil.class){
     runningThreadCount--;
     if(runningThreadCount<1){
      for(int i=1;i<=threadCount;i++){
       File file =new File(Environment.getExternalStorageDirectory(),i+".txt");
       //删除成功,返回true
       file.delete();
      }
     }
    }
   }
  }
 }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值