一.基础知识

1.什么是线程?什么是进程?它们之间的关系?

可以参考之前的一篇文章:java核心知识点学习----并发和并行的区别,进程和线程的区别,如何创建线程和线程的四种状态,什么是线程计时器

简单说一个进程可以由多个线程组成,一个操作系统可以多个进程,它们都是可以同时进行工作的.

2.什么是下载?如何多线程进行下载?如何断点续传?

广义上说,凡是在屏幕上看到的不属于本地计算机上的内容,皆是通过"下载"得来。狭义上人们只认为那些自定义了下载文件的本地磁盘存储位置的操作才是"下载";。

WEB下载方式分为HTTP与FTP两种类型,它们分别是Hyper Text Transportation Protocol(超文本传输协议)与File Transportation Protocol(文件传输协议)的缩写,它们是计算机之间交换数据的方式,也是两种最经典的下载方式,该下载方式原理非常简单,就是用户两种规则(协议)和提供文件的服务器取得联系并将文件搬到自己的计算机中来,从而实现下载的功能。
多线程下载,即是一个文件能过多个线程进行下载;而断点续传说的是当一个文件下载到一半时突然由于某个原因下载中断了,比如突然电脑关机了,那么当再开机时已经下载到一半的文件不需要重头开始,而是接着下载;其原理很简单:首先,下载中断时记住上一个时点下载的位置,然后接着这个位置继续下载,这个继续下载可以是人手工触发的也可以是程序运行时自动识别进行下载的.

3.什么是RandomAccessFile?

RandomAccessFile的唯一父类是Object,与其他流父类不同。是用来访问那些保存数据记录的文件的,这样你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。

RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream粘起来,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )。此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件,从这一点上看,假如RandomAccessFile继承了DataInputStream,它也许会干得更好。

所以,本例中是利用RandomAccessFile的seek记住上次的访问记录,然后接着上次的访问进行下载的.

RandomAccess直译过来是随机访问,这样理解很容易造成困扰,即然是随机的,那么又怎么来控制进度呢?

由Random这个单词的示意:

  1. random
  2. [ 'rændəm ]
  3. adj. 随意的,任意,随机的,随机挑选的,任意随机的,胡乱任意的,随机性,任意的,胡乱的,随便的;(话等)信口乱说的;(人等)偶然碰到的,随意选择的,随便,无关紧要的,漫不经心的

可以看到其还有任意的意思,这就表明可以访问文件的任意位置,这样就能解释为何可以断点续传了.

二.程序实现

这里以tomcat的下载为例:http://tomcat.apache.org/download-70.cgi#7.0.54 (tomcat目前最新的版本是7.0.54,2014-07-02)

F12打开chrome的WebDeveloper NetWork窗口,然后点击下载,如下图所示:

这里需要注意的是Request Headers里的内容,如RequestMethod,Accept,Accept-Language,Connection等,这里在发出请求时需要将这些东西带上,这里找到下载tomcat 7.0.54的下载链接:http://mirrors.cnnic.cn/apache/tomcat/tomcat-7/v7.0.54/bin/apache-tomcat-7.0.54.zip

实现代码:DownUtil.java

  1.  
  1. package com.amos.tool;
  2.  
  3. import java.io.InputStream;
  4. import java.io.RandomAccessFile;
  5. import java.net.HttpURLConnection;
  6. import java.net.URL;
  7.  
  8. /**
  9. * Created by amosli on 14-7-2.
  10. */
  1. public class DownUtil
  2. {
  3. // 定义下载资源的路径
  4. private String path;
  5. // 指定所下载的文件的保存位置
  6. private String targetFile;
  7. // 定义需要使用多少线程下载资源
  8. private int threadNum;
  9. // 定义下载的线程对象
  10. private DownThread[] threads;
  11. // 定义下载的文件的总大小
  12. private int fileSize;
  13.  
  14. public DownUtil(String path, String targetFile, int threadNum)
  15. {
  16. this.path = path;
  17. this.threadNum = threadNum;
  18. // 初始化threads数组
  19. threads = new DownThread[threadNum];
  20. this.targetFile = targetFile;
  21. }
  22.  
  23. public void download() throws Exception
  24. {
  25. URL url = new URL(path);
  26. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  27. conn.setConnectTimeout(5 * 1000);
  28. conn.setRequestMethod("GET");
  29. conn.setRequestProperty(
  30. "Accept",
  31. "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
  32. + "application/x-shockwave-flash, application/xaml+xml, "
  33. + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
  34. + "application/x-ms-application, application/vnd.ms-excel, "
  35. + "application/vnd.ms-powerpoint, application/msword, */*");
  36. conn.setRequestProperty("Accept-Language", "zh-CN");
  37. conn.setRequestProperty("Charset", "UTF-8");
  38. conn.setRequestProperty("Connection", "Keep-Alive");
  39. // 得到文件大小
  40. fileSize = conn.getContentLength();
  41. conn.disconnect();
  42. int currentPartSize = fileSize / threadNum + 1;//这里不必一定要加1,不加1也可以
  43. RandomAccessFile file = new RandomAccessFile(targetFile, "rw");
  44. // 设置本地文件的大小
  45. file.setLength(fileSize);
  46. file.close();
  47. for (int i = 0; i < threadNum; i++)
  48. {
  49. // 计算每条线程的下载的开始位置
  50. int startPos = i * currentPartSize;
  51. // 每个线程使用一个RandomAccessFile进行下载
  52. RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw");
  53. // 定位该线程的下载位置
  54. currentPart.seek(startPos);
  55. // 创建下载线程
  56. threads[i] = new DownThread(startPos, currentPartSize, currentPart);
  57. // 启动下载线程
  58. threads[i].start();
  59. }
  60. }
  61.  
  62. // 获取下载的完成百分比
  63. public double getCompleteRate()
  64. {
  65. // 统计多条线程已经下载的总大小
  66. int sumSize = 0;
  67. for (int i = 0; i < threadNum; i++)
  68. {
  69. sumSize += threads[i].length;
  70. }
  71. // 返回已经完成的百分比
  72. return sumSize * 1.0 / fileSize;
  73. }
  74.  
  75. private class DownThread extends Thread
  76. {
  77. // 当前线程的下载位置
  78. private int startPos;
  79. // 定义当前线程负责下载的文件大小
  80. private int currentPartSize;
  81. // 当前线程需要下载的文件块
  82. private RandomAccessFile currentPart;
  83. // 定义已经该线程已下载的字节数
  84. public int length;
  85.  
  86. public DownThread(int startPos, int currentPartSize,RandomAccessFile currentPart)
  87. {
  88. this.startPos = startPos;
  89. this.currentPartSize = currentPartSize;
  90. this.currentPart = currentPart;
  91. }
  92.  
  93. @Override
  94. public void run()
  95. {
  96. try
  97. {
  98. URL url = new URL(path);
  99. HttpURLConnection conn = (HttpURLConnection)url.openConnection();
  100. conn.setConnectTimeout(5 * 1000);
  101. conn.setRequestMethod("GET");
  102. conn.setRequestProperty(
  103. "Accept",
  104. "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
  105. + "application/x-shockwave-flash, application/xaml+xml, "
  106. + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
  107. + "application/x-ms-application, application/vnd.ms-excel, "
  108. + "application/vnd.ms-powerpoint, application/msword, */*");
  109. conn.setRequestProperty("Accept-Language", "zh-CN");
  110. conn.setRequestProperty("Charset", "UTF-8");
  111. InputStream inStream = conn.getInputStream();
  112. // 跳过startPos个字节,表明该线程只下载自己负责哪部分文件。
  113. inStream.skip(this.startPos);
  114. byte[] buffer = new byte[1024];
  115. int hasRead = 0;
  116. // 读取网络数据,并写入本地文件
  117. while (length < currentPartSize
  118. && (hasRead = inStream.read(buffer)) != -1)
  119. {
  120. currentPart.write(buffer, 0, hasRead);
  121. // 累计该线程下载的总大小
  122. length += hasRead;
  123. }
  124. currentPart.close();
  125. inStream.close();
  126. }
  127. catch (Exception e)
  128. {
  129. e.printStackTrace();
  130. }
  131. }
  132. }
  133. }

测试:DownUtilTest.java

  1. package com.amos;
  2.  
  3. import com.amos.tool.DownUtil;
  4. import org.omg.PortableServer.THREAD_POLICY_ID;
  5.  
  6. /**
  7. * Created by amosli on 14-7-2.
  8. */
  9. public class DownUtilTest {
  10.  
  11. public static void main(String args[]) throws Exception {
  12. final DownUtil downUtil = new DownUtil("http://mirrors.cnnic.cn/apache/tomcat/tomcat-7/v7.0.54/bin/apache-tomcat-7.0.54.zip", "tomcat-7.0.54.zip", 3);
  13.  
  14. downUtil.download();
  15.  
  16. new Thread(new Runnable() {
  17. @Override
  18. public void run() {
  19. while(downUtil.getCompleteRate()<1){
  20. System.out.println("已完成:"+downUtil.getCompleteRate());
  21. try {
  22. Thread.sleep(100);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26.  
  27. }
  28. }
  29. }).start();
  30. }
  31.  
  32. }

结果:

下载我的头像:

  1. final com.amos.DownUtil downUtil = new com.amos.DownUtil("http://pic.cnitblog.com/avatar/534352/20131215160918.png", "amosli.png", 2);

Java--使用多线程下载,断点续传技术原理(RandomAccessFile)的更多相关文章

  1. 图解:HTTP 范围请求,助力断点续传、多线程下载的核心原理

    题图:by Charles Loyer 一.序 Hi,大家好,我是承香墨影! HTTP 协议在网络知识中占据了重要的地位,HTTP 协议最基础的就是请求和响应的报文,而报文又是由报文头(Header) ...

  2. Java实现多线程下载,支持断点续传

    完整代码:https://github.com/iyuanyb/Downloader 多线程下载及断点续传的实现是使用 HTTP/1.1 引入的 Range 请求参数,可以访问Web资源的指定区间的内 ...

  3. android 多线程下载 断点续传

    来源:网易云课堂Android极客班第八次作业练习 练习内容: 多线程 asyncTask handler 多线程下载的原理 首先获取到目标文件的大小,然后在磁盘上申请一块空间用于保存目标文件,接着把 ...

  4. Java实现多线程下载

    package cn.test.DownLoad; import java.io.File; import java.io.InputStream; import java.io.RandomAcce ...

  5. HTTP多线程下载+断点续传(libcurl库)

    目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_perform 函数说明(error 状态码) 五.lib ...

  6. Java实现多线程下载 URL以及URLConnection

    主线程: public class MultiThreadDown { public static void main(String[] args) throws Exception{ //初始化Do ...

  7. Java多线程下载器FileDownloader(支持断点续传、代理等功能)

    前言 在我的任务清单中,很早就有了一个文件下载器,但一直忙着没空去写.最近刚好放假,便抽了些时间完成了下文中的这个下载器. 介绍 同样的,还是先上效果图吧. Jar包地址位于 FileDownload ...

  8. Java开发之多线程下载和断点续传

    代码实现了多线程下载和断点续传功能 import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream ...

  9. java多线程下载和断点续传

    java多线程下载和断点续传,示例代码只实现了多线程,断点只做了介绍.但是实际测试结果不是很理想,不知道是哪里出了问题.所以贴上来请高手修正. [Java]代码 import java.io.File ...

随机推荐

  1. swift3.0 创建一个app引导页面

    swift毕竟不像是oc ,第三方的框架很多,更何况是3.0,自己动手写了个引导页面,看得上我代码的麻友可以拿去用 引导页面有三个部分构成,scrollview用语切换引导视图,pageControl ...

  2. JS中Array详细用法

    1.数组的创建 var name= new Array(); //创建一个数组 name[0]="zhangsan";   //给数组赋值 name[1]="lisi&q ...

  3. C语言PIC32 serial bootloader和C#语言bootloader PC端串口通信程序

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 今天介绍下我新完成的为 ...

  4. 是德科技完成对Anite的收购

    是德科技公司(NYSE:KEYS)日前宣布已经完成对Anite 的收购行动.Anite 是业界领先的无线研发软件解决方案供应商.是德科技通过支付大约6 亿美元现金将其收入麾下,旨在支持是德科技发展无线 ...

  5. java后台如何获取String 类型 json里的字段值

    首先把获取到的数据转为json String sbody=Json.getGson().toJson(resp.getResponseBody()); Huanxin 这个类是 json数据对应字段的 ...

  6. SpringBoot-Learning

    SpringBoot-Learning 本项目内容为Spring Boot教程程序样例. 作者博客:http://blog.didispace.com Spring Boot系列博文:http://b ...

  7. Badboy录制脚本参数化

    Jmeter录制脚本多采用Badboy工具 简单参数修改 第一步,先录制场景: a.开始录制,打开浏览器www.sogou.com b.在输入框中输入文字“Badboy" c.回车,关闭录制 ...

  8. golang中string以及slice之间的一些问题

    好记性不如烂笔头o_O slice切片不会开辟新的空间 a := []int{0,1,2,3} b := make([]int, 8) b = a[:] b[2] = 9 fmt.Println(a) ...

  9. 【原】无规矩,不方圆——说一说正则里的exec()和test()

    今天一大早遇就遇到一件诡异的事儿,可能是思绪还没有澄静下来,一下子没反应过来.事情是这样的: 模板: <input class="name" type="text& ...

  10. 【原】git如何删除本地和远程的仓库

    今天操作git时遇到一个小问题:如何删除本地和远程的仓库,在网上巴拉一番解决了这个问题. 方法1: $rm testfile$git add -u $git commit -m "delet ...