一.基础知识

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)的更多相关文章

  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. mysql连接查询,封装mysql函数

    连接查询 交叉连接语法: select  * | 字段列表 from 表1  cross join 表2 内连接: select *|字段列表 from 左表 inner join 右表 on 左表. ...

  2. IntelliJ IDEA 自动化工具安装并添加自动化测试框架

    IntelliJ IDEA是一个用于开发人员开发和测试人员自动化测试的测试工具,类似于eclipse. 优点:插件多自身可以携带,自身携带cucumber自动化测试框架,类似于junit一样 缺点:r ...

  3. wampserver配置域名

    装载自http://blog.csdn.net/znb26/article/details/51204313

  4. ARM仿真器

    ARM仿真器需要将调试换成simulator,其他JLink设置不变.否则会提示无法装载.flash文件 在keil下开发的设置

  5. Struts2框架之-Struts2的标签

    Struts2包含哪些标签? 解答: A: <s:a href=”"></s:a>—–超链接,类似于html里的<a></a> <s:a ...

  6. Matlab中的一些小技巧

    (转于它处,仅供参考) 1.. Ctrl+C 中断正在执行的操作 如果程序不小心进入死循环,或者计算时间太长,可以在命令窗口中使用Ctrl+c来中断.MATLAB这时可能正疲于应付,响应会有些滞后. ...

  7. 在Ubuntu上安装Mysql For Python

    安装: 首先安装pip,并且把pip更新到最小版本 apt-get install python-pip pip install -U pip 安装mysql开发包 apt-get install p ...

  8. formValidator表单验证示例

    <script type="text/javascript">$(document).ready(function(){ $.formValidator.initCon ...

  9. java位运算符介绍

    位操作是程序设计中对位模式按位或二进制数的一元和二元操作. 在许多古老的微处理器上, 位运算比加减运算略快, 通常位运算比乘除法运算要快很多. 在现代架构中, 情况并非如此:位运算的运算速度通常与加法 ...

  10. 深入理解jQuery中的Deferred

    引入 1  在开发的过程中,我们经常遇到某些耗时很长的javascript操作,并且伴随着大量的异步. 2  比如我们有一个ajax的操作,这个ajax从发出请求到接收响应需要5秒,在这5秒内我们可以 ...