Java--使用多线程下载,断点续传技术原理(RandomAccessFile)
一.基础知识
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这个单词的示意:
- random
- [ 'rændəm ]
- 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
- package com.amos.tool;
- import java.io.InputStream;
- import java.io.RandomAccessFile;
- import java.net.HttpURLConnection;
- import java.net.URL;
- /**
- * Created by amosli on 14-7-2.
- */
- public class DownUtil
- {
- // 定义下载资源的路径
- private String path;
- // 指定所下载的文件的保存位置
- private String targetFile;
- // 定义需要使用多少线程下载资源
- private int threadNum;
- // 定义下载的线程对象
- private DownThread[] threads;
- // 定义下载的文件的总大小
- private int fileSize;
- public DownUtil(String path, String targetFile, int threadNum)
- {
- this.path = path;
- this.threadNum = threadNum;
- // 初始化threads数组
- threads = new DownThread[threadNum];
- this.targetFile = targetFile;
- }
- public void download() throws Exception
- {
- URL url = new URL(path);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setConnectTimeout(5 * 1000);
- conn.setRequestMethod("GET");
- conn.setRequestProperty(
- "Accept",
- "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
- + "application/x-shockwave-flash, application/xaml+xml, "
- + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
- + "application/x-ms-application, application/vnd.ms-excel, "
- + "application/vnd.ms-powerpoint, application/msword, */*");
- conn.setRequestProperty("Accept-Language", "zh-CN");
- conn.setRequestProperty("Charset", "UTF-8");
- conn.setRequestProperty("Connection", "Keep-Alive");
- // 得到文件大小
- fileSize = conn.getContentLength();
- conn.disconnect();
- int currentPartSize = fileSize / threadNum + 1;//这里不必一定要加1,不加1也可以
- RandomAccessFile file = new RandomAccessFile(targetFile, "rw");
- // 设置本地文件的大小
- file.setLength(fileSize);
- file.close();
- for (int i = 0; i < threadNum; i++)
- {
- // 计算每条线程的下载的开始位置
- int startPos = i * currentPartSize;
- // 每个线程使用一个RandomAccessFile进行下载
- RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw");
- // 定位该线程的下载位置
- currentPart.seek(startPos);
- // 创建下载线程
- threads[i] = new DownThread(startPos, currentPartSize, currentPart);
- // 启动下载线程
- threads[i].start();
- }
- }
- // 获取下载的完成百分比
- public double getCompleteRate()
- {
- // 统计多条线程已经下载的总大小
- int sumSize = 0;
- for (int i = 0; i < threadNum; i++)
- {
- sumSize += threads[i].length;
- }
- // 返回已经完成的百分比
- return sumSize * 1.0 / fileSize;
- }
- private class DownThread extends Thread
- {
- // 当前线程的下载位置
- private int startPos;
- // 定义当前线程负责下载的文件大小
- private int currentPartSize;
- // 当前线程需要下载的文件块
- private RandomAccessFile currentPart;
- // 定义已经该线程已下载的字节数
- public int length;
- public DownThread(int startPos, int currentPartSize,RandomAccessFile currentPart)
- {
- this.startPos = startPos;
- this.currentPartSize = currentPartSize;
- this.currentPart = currentPart;
- }
- @Override
- public void run()
- {
- try
- {
- URL url = new URL(path);
- HttpURLConnection conn = (HttpURLConnection)url.openConnection();
- conn.setConnectTimeout(5 * 1000);
- conn.setRequestMethod("GET");
- conn.setRequestProperty(
- "Accept",
- "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
- + "application/x-shockwave-flash, application/xaml+xml, "
- + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
- + "application/x-ms-application, application/vnd.ms-excel, "
- + "application/vnd.ms-powerpoint, application/msword, */*");
- conn.setRequestProperty("Accept-Language", "zh-CN");
- conn.setRequestProperty("Charset", "UTF-8");
- InputStream inStream = conn.getInputStream();
- // 跳过startPos个字节,表明该线程只下载自己负责哪部分文件。
- inStream.skip(this.startPos);
- byte[] buffer = new byte[1024];
- int hasRead = 0;
- // 读取网络数据,并写入本地文件
- while (length < currentPartSize
- && (hasRead = inStream.read(buffer)) != -1)
- {
- currentPart.write(buffer, 0, hasRead);
- // 累计该线程下载的总大小
- length += hasRead;
- }
- currentPart.close();
- inStream.close();
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- }
- }
- }
测试:DownUtilTest.java
- package com.amos;
- import com.amos.tool.DownUtil;
- import org.omg.PortableServer.THREAD_POLICY_ID;
- /**
- * Created by amosli on 14-7-2.
- */
- public class DownUtilTest {
- public static void main(String args[]) throws Exception {
- 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);
- downUtil.download();
- new Thread(new Runnable() {
- @Override
- public void run() {
- while(downUtil.getCompleteRate()<1){
- System.out.println("已完成:"+downUtil.getCompleteRate());
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }).start();
- }
- }
结果:
下载我的头像:
- final com.amos.DownUtil downUtil = new com.amos.DownUtil("http://pic.cnitblog.com/avatar/534352/20131215160918.png", "amosli.png", 2);
Java--使用多线程下载,断点续传技术原理(RandomAccessFile)的更多相关文章
- 图解:HTTP 范围请求,助力断点续传、多线程下载的核心原理
题图:by Charles Loyer 一.序 Hi,大家好,我是承香墨影! HTTP 协议在网络知识中占据了重要的地位,HTTP 协议最基础的就是请求和响应的报文,而报文又是由报文头(Header) ...
- Java实现多线程下载,支持断点续传
完整代码:https://github.com/iyuanyb/Downloader 多线程下载及断点续传的实现是使用 HTTP/1.1 引入的 Range 请求参数,可以访问Web资源的指定区间的内 ...
- android 多线程下载 断点续传
来源:网易云课堂Android极客班第八次作业练习 练习内容: 多线程 asyncTask handler 多线程下载的原理 首先获取到目标文件的大小,然后在磁盘上申请一块空间用于保存目标文件,接着把 ...
- Java实现多线程下载
package cn.test.DownLoad; import java.io.File; import java.io.InputStream; import java.io.RandomAcce ...
- HTTP多线程下载+断点续传(libcurl库)
目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_perform 函数说明(error 状态码) 五.lib ...
- Java实现多线程下载 URL以及URLConnection
主线程: public class MultiThreadDown { public static void main(String[] args) throws Exception{ //初始化Do ...
- Java多线程下载器FileDownloader(支持断点续传、代理等功能)
前言 在我的任务清单中,很早就有了一个文件下载器,但一直忙着没空去写.最近刚好放假,便抽了些时间完成了下文中的这个下载器. 介绍 同样的,还是先上效果图吧. Jar包地址位于 FileDownload ...
- Java开发之多线程下载和断点续传
代码实现了多线程下载和断点续传功能 import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream ...
- java多线程下载和断点续传
java多线程下载和断点续传,示例代码只实现了多线程,断点只做了介绍.但是实际测试结果不是很理想,不知道是哪里出了问题.所以贴上来请高手修正. [Java]代码 import java.io.File ...
随机推荐
- 用 pyvenv 创建几个不相互影响的python虚拟环境
IN MY UBUNTU python2的环境控制: sudo apt-get install virtualenv 创建: virtualenv --no-site-packages [环境搭建目 ...
- vs2015 企业版 专业版 密钥
亲测可用 专业版:HMGNV-WCYXV-X7G9W-YCX63-B98R2企业版:HM6NR-QXX7C-DFW2Y-8B82K-WTYJV
- html, xhtml和xml
html, xhtml和xml 1.定义及特点: 1) html:Hyper Text Markup Language 超文本标记语言 是最早写网页的语言,但编码不规范,主要用于控制数据的显示和外观. ...
- Hash函数及其应用
本文部分内容摘自网络,参考资料链接会在文后给出,在此感谢原作者的分享. 计算理论中,没有Hash函数的说法,只有单向函数的说法.所谓的单向函数,是一个复杂的定义,大家可以去看计算理论或者密码学方面的数 ...
- Python应用科学计算和图表绘制
今天更新了两个python模块,一个是用于科学计算的numpy模块,另一个是用于绘图的matplotlib模块 python安装模块还是很方便的,安装了pip之后直接使用"pip insta ...
- jmeter上传文件搞了一天,才搞定,没高人帮忙效率就是低,赶紧记下来,以备后用
先用谷歌浏览器抓包,抓到的包类似这样: 在jmeter里添加一个http请求,配置好参数,方法,端口,路径等, 勾选 在“同请求一起发送参数”里填写上面抓包的部分数据: 分别对应录入,勾选“编码” 我 ...
- 使用onclick跳转到其他页面/跳转到指定url
☆如果是本页显示可以直接用location,方法如下: ①onclick="javascript:window.location.href='URL'" ②onclick=&quo ...
- js back动作
history.back(-1):直接返回当前页的上一页,数据全部消息,是个新页面 history.go(-1):也是返回当前页的上一页,不过表单里的数据全部还在 history.back(0) 刷新 ...
- 从append追加的<tr>里传ID参数给js函数
今天这个小问题几乎把我整崩溃 $.each(data.list, function (index, item) { i++; shenhe = "待审核"; tixing = it ...
- Hibernate的查询方式总结
Hibernate的查询方式大体有三种,分别是HQL QBC和SQL三种.在网上查阅一一些资料,做了一个简单的总结. 1. SQL sql 是面向数据库表查询,from 后面跟的是表名,where 后 ...