代码中已经加入了注释,需要的朋友可以直接参考代码中的注释。下面直接上功能实现的主要代码:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /*
* Encode:UTF-8
*
* Author:zhiming.xu
*
* 多线程的断点下载程序,根据输入的url和指定线程数,来完成断点续传功能。
*
* 每个线程支负责某一小段的数据下载;再通过RandomAccessFile完成数据的整合。
*/
public class MultiTheradDownLoad { private String filepath = null;
private String filename = null;
private String tmpfilename = null; private int threadNum = 0; private CountDownLatch latch = null;//设置一个计数器,代码内主要用来完成对缓存文件的删除 private long fileLength = 0l;
private long threadLength = 0l;
private long[] startPos;//保留每个线程下载数据的起始位置。
private long[] endPos;//保留每个线程下载数据的截止位置。 private boolean bool = false; private URL url = null; //有参构造函数,先构造需要的数据
public MultiTheradDownLoad(String filepath, int threadNum) {
this.filepath = filepath;
this.threadNum = threadNum;
startPos = new long[this.threadNum];
endPos = new long[this.threadNum];
latch = new CountDownLatch(this.threadNum);
} /*
* 组织断点续传功能的方法
*/
public void downloadPart() { File file = null;
File tmpfile = null;
HttpURLConnection httpcon = null; //在请求url内获取文件资源的名称;此处没考虑文件名为空的情况,此种情况可能需使用UUID来生成一个唯一数来代表文件名。
filename = filepath.substring(filepath.lastIndexOf('/') + 1, filepath
.contains("?") ? filepath.lastIndexOf('?') : filepath.length());
tmpfilename = filename + "_tmp"; try {
url = new URL(filepath);
httpcon = (HttpURLConnection) url.openConnection(); setHeader(httpcon);
fileLength = httpcon.getContentLengthLong();//获取请求资源的总长度。 file = new File(filename);
tmpfile = new File(tmpfilename); threadLength = fileLength / threadNum;//每个线程需下载的资源大小。
System.out.println("fileName: " + filename + " ," + "fileLength= "
+ fileLength + " the threadLength= " + threadLength); if (file.exists() && file.length() == fileLength) {
System.out
.println("the file you want to download has exited!!");
return;
} else {
setBreakPoint(startPos, endPos, tmpfile);
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < threadNum; i++) {
exec.execute(new DownLoadThread(startPos[i], endPos[i],
this, i, tmpfile, latch));
}
latch.await();//当你的计数器减为0之前,会在此处一直阻塞。
exec.shutdown();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} if (file.length() == fileLength) {
if (tmpfile.exists()) {
System.out.println("delect the temp file!!");
tmpfile.delete();
}
}
} /*
* 断点设置方法,当有临时文件时,直接在临时文件中读取上次下载中断时的断点位置。没有临时文件,即第一次下载时,重新设置断点。
*
* rantmpfile.seek()跳转到一个位置的目的是为了让各个断点存储的位置尽量分开。
*
* 这是实现断点续传的重要基础。
*/
private void setBreakPoint(long[] startPos, long[] endPos, File tmpfile) {
RandomAccessFile rantmpfile = null;
try {
if (tmpfile.exists()) {
System.out.println("the download has continued!!");
rantmpfile = new RandomAccessFile(tmpfile, "rw");
for (int i = 0; i < threadNum; i++) {
rantmpfile.seek(8 * i + 8);
startPos[i] = rantmpfile.readLong(); rantmpfile.seek(8 * (i + 1000) + 16);
endPos[i] = rantmpfile.readLong(); System.out.println("the Array content in the exit file: ");
System.out.println("thre thread" + (i + 1) + " startPos:"
+ startPos[i] + ", endPos: " + endPos[i]);
}
} else {
System.out.println("the tmpfile is not available!!");
rantmpfile = new RandomAccessFile(tmpfile, "rw"); //最后一个线程的截止位置大小为请求资源的大小
for (int i = 0; i < threadNum; i++) {
startPos[i] = threadLength * i;
if (i == threadNum - 1) {
endPos[i] = fileLength;
} else {
endPos[i] = threadLength * (i + 1) - 1;
} rantmpfile.seek(8 * i + 8);
rantmpfile.writeLong(startPos[i]); rantmpfile.seek(8 * (i + 1000) + 16);
rantmpfile.writeLong(endPos[i]); System.out.println("the Array content: ");
System.out.println("thre thread" + (i + 1) + " startPos:"
+ startPos[i] + ", endPos: " + endPos[i]);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (rantmpfile != null) {
rantmpfile.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} /*
* 实现下载功能的内部类,通过读取断点来设置向服务器请求的数据区间。
*/
class DownLoadThread implements Runnable { private long startPos;
private long endPos;
private MultiTheradDownLoad task = null;
private RandomAccessFile downloadfile = null;
private int id;
private File tmpfile = null;
private RandomAccessFile rantmpfile = null;
private CountDownLatch latch = null; public DownLoadThread(long startPos, long endPos,
MultiTheradDownLoad task, int id, File tmpfile,
CountDownLatch latch) {
this.startPos = startPos;
this.endPos = endPos;
this.task = task;
this.tmpfile = tmpfile;
try {
this.downloadfile = new RandomAccessFile(this.task.filename,
"rw");
this.rantmpfile = new RandomAccessFile(this.tmpfile, "rw");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
this.id = id;
this.latch = latch;
} @Override
public void run() { HttpURLConnection httpcon = null;
InputStream is = null;
int length = 0; System.out.println("the thread " + id + " has started!!"); while (true) {
try {
httpcon = (HttpURLConnection) task.url.openConnection();
setHeader(httpcon); //防止网络阻塞,设置指定的超时时间;单位都是ms。超过指定时间,就会抛出异常
httpcon.setReadTimeout(20000);//读取数据的超时设置
httpcon.setConnectTimeout(20000);//连接的超时设置 if (startPos < endPos) { //向服务器请求指定区间段的数据,这是实现断点续传的根本。
httpcon.setRequestProperty("Range", "bytes=" + startPos
+ "-" + endPos); System.out
.println("Thread " + id
+ " the total size:---- "
+ (endPos - startPos)); downloadfile.seek(startPos); if (httpcon.getResponseCode() != HttpURLConnection.HTTP_OK
&& httpcon.getResponseCode() != HttpURLConnection.HTTP_PARTIAL) {
this.task.bool = true;
httpcon.disconnect();
downloadfile.close();
System.out.println("the thread ---" + id
+ " has done!!");
latch.countDown();//计数器自减
break;
} is = httpcon.getInputStream();//获取服务器返回的资源流
long count = 0l;
byte[] buf = new byte[1024]; while (!this.task.bool && (length = is.read(buf)) != -1) {
count += length;
downloadfile.write(buf, 0, length); //不断更新每个线程下载资源的起始位置,并写入临时文件;为断点续传做准备
startPos += length;
rantmpfile.seek(8 * id + 8);
rantmpfile.writeLong(startPos);
}
System.out.println("the thread " + id
+ " total load count: " + count); //关闭流
is.close();
httpcon.disconnect();
downloadfile.close();
rantmpfile.close();
}
latch.countDown();//计数器自减
System.out.println("the thread " + id + " has done!!");
break;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
} /*
* 为一个HttpURLConnection模拟请求头,伪装成一个浏览器发出的请求
*/
private void setHeader(HttpURLConnection con) {
con.setRequestProperty(
"User-Agent",
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3");
con.setRequestProperty("Accept-Language", "en-us,en;q=0.7,zh-cn;q=0.3");
con.setRequestProperty("Accept-Encoding", "aa");
con.setRequestProperty("Accept-Charset",
"ISO-8859-1,utf-8;q=0.7,*;q=0.7");
con.setRequestProperty("Keep-Alive", "300");
con.setRequestProperty("Connection", "keep-alive");
con.setRequestProperty("If-Modified-Since",
"Fri, 02 Jan 2009 17:00:05 GMT");
con.setRequestProperty("If-None-Match", "\"1261d8-4290-df64d224\"");
con.setRequestProperty("Cache-Control", "max-age=0");
con.setRequestProperty("Referer",
"http://www.skycn.com/soft/14857.html");
}
}

下面是测试代码:

public class DownLoadTest {

    /**
* @param args
*/
public static void main(String[] args) { String filepath = "http://127.0.0.1:8080/file/loadfile.mkv";
MultiTheradDownLoad load = new MultiTheradDownLoad(filepath ,4);
load.downloadPart();
}
}

参考资料:http://www.iteye.com/topic/427397这篇文章对断点续传的原理做了较详细的解释。

Java实现的断点续传功能的更多相关文章

  1. java视频流的断点续传功能

    项目中需要实现浏览器中视频的拖动问题解决 /** * 视频文件的断点续传功能 * @param path 文件路径 * @param request request * @param response ...

  2. Java单线程文件下载,支持断点续传功能

    前言: 程序下载文件时,有时会因为各种各样的原因下载中断,对于小文件来说影响不大,可以快速重新下载,但是下载大文件时,就会耗费很长时间,所以断点续传功能对于大文件很有必要. 文件下载的断点续传: 1. ...

  3. java+HTML5实现断点续传

    一. 大文件上传基础描述: 各种WEB框架中,对于浏览器上传文件的请求,都有自己的处理对象负责对Http MultiPart协议内容进行解析,并供开发人员调用请求的表单内容. 比如: Spring 框 ...

  4. java+大文件断点续传

    用JAVA实现大文件上传及显示进度信息 ---解析HTTP MultiPart协议 (本文提供全部源码下载,请访问 https://github.com/1269085759/up6-jsp-mysq ...

  5. chrome断点续传功能

    刚好找到了一个临时的解决方法,chrome其实已经内部实现了断点续传功能,不过应该还没完善,所以要自己打开.方法:用chrome在地址栏输入chrome://flags用搜索找到resumption( ...

  6. 转:Http下载文件类 支技断点续传功能

    using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Net ...

  7. Java 基本数据类型 sizeof 功能

    Java 基本数据类型 sizeof 功能 来源 https://blog.csdn.net/ithomer/article/details/7310008 Java基本数据类型int     32b ...

  8. Java实现发邮件功能---网易邮箱

    目录 Java实现发邮件功能 前言 开发环境 代码 效果 结束语 Java实现发邮件功能 前言 电子邮件的应用场景非常广泛,例如新用户加入,即时发送优惠清单.通过邮件找回密码.监听后台程序,出现异常自 ...

  9. Java文件操作API功能与Windows DOS命令和Linux Shell 命令类比

    Java文件操作API功能与Windows DOS命令和Linux Shell 命令类比: Unix/Linux (Bash) Windows(MS-DOS) Java 进入目录 cd cd - 创建 ...

随机推荐

  1. 用Entity Framework 来创建MySql数据库和表结构

    1. 创建VS工程 2.添加新项, 选中ADO.Net Entity Data Model模板 3.填入Host及数据库名字, 如果没有此数据库, 会提示创建 4.添加edmx后, 右击选择属性,配置 ...

  2. C#支持从自定义日期时间格式到DateTime类型

            /// <summary>         ///         /// </summary>         /// <param name=&quo ...

  3. 【转】值得推荐的C/C++框架和库

    偶然间在博客园前辈那里看到的,转载备用,日后研究. 原文链接:http://www.cnblogs.com/findumars/p/6891515.html Webbench是一个在linux下使用的 ...

  4. Haproxy 安装配置详解-端口映射

    Haproxy 简介: HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案. HAProxy特别适用于那些负载特大的web站点, ...

  5. 条款32:确定你的public继承塑模出is-a的关系

    Make sure public inheritance models "is –a " 如果令clsss D 以public的形式继承class B,你便是告诉编译器说,每一个类 ...

  6. 关于Flask使用Celery的实践经验分享

      最近大Boss反馈Celery经常出现问题,几经实践终于把问题解决了!于是乎有了这篇博客的诞生,算是一个实践经验的分享吧! 软件版本如下: Celery () Flask () RabbitMQ( ...

  7. dpdk学习笔记2

    一 了解dpdk准备知识 1 NAT NAT技术是为了缓解IPV4地址枯竭得问题,通过使用NAT技术,一个机构如学校可以只用单一得公网IP来范文互联网,在外界看来只有一台接入公网得设备.NAT分为两种 ...

  8. IIS 中的虚拟目录 和软连接

    在WIndows 中 可以这样设置 mklink /D  C:\bb C:\cc 这样 bb 就指向 CC了 bb 在IIS中的目录其实就是虚拟目录 .这样大的文件就不用存放在IIS中了,可以放在其他 ...

  9. 牛客多校3 C-Shuffle Cards(rope大法解决数组分块)

    Shuffle Cards 链接:https://www.nowcoder.com/acm/contest/141/C来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 26 ...

  10. HDU - 3499 Flight 双向SPFA+枚举中间边

    Flight Recently, Shua Shua had a big quarrel with his GF. He is so upset that he decides to take a t ...