知识大全 实现HTTP断点续传下载工具(附源代码)
Posted 文件
篇首语:幽映每白日,清辉照衣裳。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 实现HTTP断点续传下载工具(附源代码)相关的知识,希望对你有一定的参考价值。
Java网络编程从入门到精通(24):实现HTTP断点续传下载工具(附源代码) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
在前面的文章曾讨论了HTTP消息头的三个和断点继传有关的字段 一个是请求消息的字段Range 另两个是响应消息字段Accept Ranges和Content Range 其中Accept Ranges用来断定Web服务器是否支持断点继传功能 在这里为了演示如何实现断点继传功能 假设Web服务器支持这个功能 因此 我们只使用Range和Content Range来完成一个断点继传工具的开发
● 要实现一个什么样的断点续传工具?
这个断点续工具是一个单线程的下载工具 它通过参数传入一个文本文件 这个文件的格式如下
/ jpg d:\\ok jpg / jpg d:\\ok jpg / jpg d:\\ok jpg
这个文本文件的每一行是一个下载项 这个下载项分为三部分
● 要下载的Web资源的URL
● 要保存的本地文件名
● 下载的缓冲区大小(单位是字节)
使用至少一个空格来分隔这三部分 这个下载工具逐个下载这些文件 在这些文件全部下载完后程序退出
● 断点续传的工作原理
断点续传 顾名思义 就是一个文件下载了一部分后 由于服务器或客户端的原因 当前的网络连接中断了 在中断网络连接后 用户还可以再次建立网络连接来继续下载这个文件还没有下完的部分
要想实现单线程断点续传 必须在客户断保存两个数据
已经下载的字节数
下载文件的URL
一但重新建立网络连接后 就可以利用这两个数据接着未下载完的文件继续下载 在本下载工具中第一种数据就是文件已经下载的字节数 而第二个数据在上述的下载文件中保存
在继续下载时检测已经下载的字节数 假设已经下载了 个字节 那么HTTP请求消息头的Range字段被设为如下形式
Range: bytes=
HTTP响应消息头的Content Range字段被设为如下的形式
Content Range: bytes /
● 实现断点续传下载工具
一个断点续传下载程序可按如下几步实现
输入要下载文件的URL和要保存的本地文件名 并通过Socket类连接到这个URL
所指的服务器上
在客户端根据下载文件的URL和这个本地文件生成HTTP请求消息 在生成请求
消息时分为两种情况
( )第一次下载这个文件 按正常情况生成请求消息 也就是说生成不包含Range
字段的请求消息
( )以前下载过 这次是接着下载这个文件 这就进入了断点续传程序 在这种情况生成的HTTP请求消息中必须包含Range字段 由于是单线程下载 因此 这个已经下载了一部分的文件的大小就是Range的值 假设当前文件的大小是 个字节 那么将Range设成如下的值
Range bytes=
向服务器发送HTTP请求消息
接收服务器返回的HTTP响应消息
处理HTTP响应消息 在本程序中需要从响应消息中得到下载文件的总字节数 如
果是第一次下载 也就是说响应消息中不包含Content Range字段时 这个总字节数也就是Content Length字段的值 如果响应消息中不包含Content Length字段 则这个总字节数无法确定 这就是为什么使用下载工具下载一些文件时没有文件大小和下载进度的原因 如果响应消息中包含Content Range字段 总字节数就是Content Range bytes m n/k中的k 如Content Range的值为
Content Range bytes /
则总字节数为 由于本程序使用的Range值类型是得到从某个字节开始往后的所有字节 因此 当前的响应消息中的Content Range总是能返回还有多少个字节未下载 如上面的例子未下载的字节数为 + =
开始下载文件 并计算下载进度(百分比形式) 如果网络连接断开时 文件仍未下载完 重新执行第一步 也果文件已经下载完 退出程序
分析以上六个步骤得知 有四个主要的功能需要实现
生成HTTP请求消息 并将其发送到服务器 这个功能由generateHttpRequest方法来完成
分析HTTP响应消息头 这个功能由analyzeHttpHeader方法来完成
得到下载文件的实际大小 这个功能由getFileSize方法来完成
下载文件 这个功能由download方法来完成
以上四个方法均被包含在这个断点续传工具的核心类HttpDownload java中 在给出HttpDownload类的实现之前先给出一个接口DownloadEvent接口 从这个接口的名字就可以看出 它是用来处理下载过程中的事件的 下面是这个接口的实现代码
package download; public interface DownloadEvent void percent(long n); // 下载进度 void state(String s); // 连接过程中的状态切换 void viewHttpHeaders(String s); // 枚举每一个响应消息字段
从上面的代码可以看出 DownloadEvent接口中有三个事件方法 在以后的主函数中将实现这个接口 来向控制台输出相应的信息 下面给出了HttpDownload类的主体框架代码
package download; import *; import java io *; import java util *; public class HttpDownload private HashMap Headers = new HashMap(); private String stateCode; // generateHttpRequest方法 /* ananlyzeHttpHeader方法 * * addHeaderToMap方法 * * analyzeFirstLine方法 */ // getFileSize方法 // download方法 /* getHeader方法 * * getIntHeader方法 */
上面的代码只是HttpDownload类的框架代码 其中的方法并未直正实现 我们可以从中看出第 和 行就是上述的四个主要的方法 在 和 行的addHeaderToMap和analyzeFirstLine方法将在analyzeHttpHeader方法中用到 而 和 行的getHeader和getIntHeader方法在getFileSize和download方法都会用到 上述的八个方法的实现都会在后面给出
private void generateHttpRequest(OutputStream out String host String path long startPos) throws IOException OutputStreamWriter writer = new OutputStreamWriter(out); writer write( GET + path + HTTP/ \\r\\n ); writer write( Host: + host + \\r\\n ); writer write( Accept: */*\\r\\n ); writer write( User Agent: My First Http Download\\r\\n ); if (startPos > ) // 如果是断点续传 加入Range字段 writer write( Range: bytes= + String valueOf(startPos) + \\r\\n ); writer write( Connection: close\\r\\n\\r\\n ); writer flush();
这个方法有四个参数
OutputStream out
使用Socket对象的getOutputStream方法得到的输出流
String host
下载文件所在的服务器的域名或IP
String path
下载文件在服务器上的路径 也就跟在GET方法后面的部分
long startPos
从文件的startPos位置开始下载 如果startPos为 则不生成Range字段
private void analyzeHttpHeader(InputStream inputStream DownloadEvent de) throws Exception String s = ; byte b = ; while (true) b = (byte) inputStream read(); if (b == \\r ) b = (byte) inputStream read(); if (b == \\n ) if (s equals( )) break; de viewHttpHeaders(s); addHeaderToMap(s); s = ; else s += (char) b; private void analyzeFirstLine(String s) String[] ss = s split( [ ]+ ); if (ss length > ) stateCode = ss[ ]; private void addHeaderToMap(String s) int index = s indexOf( : ); if (index > ) (s substring( index) s substring(index + ) trim()); else analyzeFirstLine(s);
第&# ;行 analyzeHttpHeader方法的实现 这个方法有两个参数 其中inputStream是用Socket对象的getInputStream方法得到的输入流 这个方法是直接使用字节流来分析的HTTP响应头(主要是因为下载的文件不一定是文本文件 因此 都统一使用字节流来分析和下载) 每两个 r n 之间的就是一个字段和字段值对 在 行调用了DownloadEvent接口的viewHttpHeaders事件方法来枚举每一个响应头字段
第&# ;行 analyzeFirstLine方法的实现 这个方法的功能是分析响应消息头的第一行 并从中得到状态码后 将其保存在stateCode变量中 这个方法的参数s就是响应消息头的第一行
第&# ;行 addHeaderToMap方法的实现 这个方法的功能是将每一个响应请求消息字段和字段值加到在HttpDownload类中定义的Headers哈希映射中 在第 行查找每一行消息头是否包含 : 如果包含 : 这一行必是消息头的第一行 因此 在第 行调用了analyzeFirstLine方法从第一行得到响应状态码
private String getHeader(String header) return (String) (header); private int getIntHeader(String header) return Integer parseInt(getHeader(header));
这两个方法将会在getFileSize和download中被调用 它们的功能是从响应消息中根据字段字得到相应的字段值 getHeader得到字符串形式的字段值 而getIntHeader得到整数型的字段值
public long getFileSize() long length = ; try length = getIntHeader( Content Length ); String[] ss = getHeader( Content Range ) split( [/] ); if (ss length > ) length = Integer parseInt(ss[ ]); else length = ; catch (Exception e) return length;
getFileSize方法的功能是得到下载文件的实际大小 首先在 行通过Content Length得到了当前响应消息的实体内容大小 然后在 行得到了Content Range字段值所描述的文件的实际大小( 后面的值) 如果Content Range字段不存在 则文件的实际大小就是Content Length字段的值 如果Content Length字段也不存在 则返回 表示文件实际大小无法确定
public void download(DownloadEvent de String url String localFN int cacheSize) throws Exception File file = new File(localFN); long finishedSize = ; long fileSize = ; // localFN所指的文件的实际大小 FileOutputStream fileOut = new FileOutputStream(localFN true); URL myUrl = new URL(url); Socket socket = new Socket(); byte[] buffer = new byte[cacheSize]; // 下载数据的缓冲 if (file exists()) finishedSize = file length(); // 得到要下载的Web资源的端口号 未提供 默认是 int port = (myUrl getPort() == ) ? : myUrl getPort(); de state( 正在连接 + myUrl getHost() + : + String valueOf(port)); nnect(new InetSocketAddress(myUrl getHost() port) ); de state( 连接成功! ); // 产生HTTP请求消息 generateHttpRequest(socket getOutputStream() myUrl getHost() myUrl getPath() finishedSize); InputStream inputStream = socket getInputStream(); // 分析HTTP响应消息头 analyzeHttpHeader(inputStream de); fileSize = getFileSize(); // 得到下载文件的实际大小 if (finishedSize >= fileSize) return; else if (finishedSize > && stateCode equals( )) return; if (stateCode charAt( ) != ) throw new Exception( 不支持的响应码 ); int n = ; long m = finishedSize; while ((n = inputStream read(buffer)) != ) fileOut write(buffer n); m += n; if (fileSize != ) de percent(m * / fileSize); fileOut close(); socket close();
download方法是断点续传工具的核心方法 它有四个参数
DownloadEvent de
用于处理下载事件的接口
String url
要下载文件的URL
String localFN
要保存的本地文件名 可以用这个文件的大小来确定已经下载了多少个字节
int cacheSize
下载数据的缓冲区 也就是一次从服务器下载多个字节 这个值不宜太小 因为 频繁地从服务器下载数据 会降低网络的利用率 一般可以将这个值设为 ( K)
为了分析下载文件的url 在 行使用了URL类 这个类在以后还会介绍 在这里只要知道使用这个类可以将使用各种协议的url(包括HTTP和FTP协议)的各个部分分解 以便单独使用其中的一部分
第行 根据文件的实际大小和已经下载的字节数(finishedSize)来判断是否文件是否已经下载完成 当文件的实际大小无法确定时 也就是fileSize返回 时 不能下载
第行 如果文件已经下载了一部分 并且返回的状态码仍是 (应该是 ) 则表明服务器并不支持断点续传 当然 这可以根据另一个字段Accept Ranges来判断
第行 由于本程序未考虑重定向(状态码是 xx)的情况 因此 在使用download时 不要下载返回 xx状态码的Web资源
第&# ;行 开始下载文件 第 行调用DownloadEvent的percent方法来返回下载进度
package download; import java io *; class NewProgress implements DownloadEvent private long oldPercent = ; public void percent(long n) if (n > oldPercent) System out print( [ + String valueOf(n) + %] ); oldPercent = n; public void state(String s) System out println(s); public void viewHttpHeaders(String s) System out println(s); public class Main public static void main(String[] args) throws Exception DownloadEvent progress = new NewProgress(); if (args length < ) System out println( 用法 java class 下载文件名 ); return; FileInputStream fis = new FileInputStream(args[ ]); BufferedReader fileReader = new BufferedReader(new InputStreamReader( fis)); String s = ; String[] ss; while ((s = fileReader readLine()) != null) try ss = s split( [ ]+ ); if (ss length > ) System out println( \\r\\n ); System out println( 正在下载: + ss[ ]); System out println( 文件保存位置: + ss[ ]); System out println( 下载缓冲区大小: + ss[ ]); System out println( ); HttpDownload Download = new HttpDownload(); (new NewProgress() ss[ ] ss[ ] Integer parseInt(ss[ ])); catch (Exception e) System out println(e getMessage()); fileReader close();
第&# ;行 实现DownloadEvent接口的NewDownloadEvent类 用于在Main函数里接收相应事件传递的数据
第&# ;行 下载工具的Main方法 在这个Main方法里 打开下载资源列表文件 逐行下载相应的Web资源
测试
假设download txt在当前目录中 内容如下
;HttpSimulator rar ;designpatterns rar download rar
这两个URL是在本机的Web服务器(如IIS)的虚拟目录中的两个文件 将它们下载在D盘根目录
运行下面的命令
java download Main download txt
运行的结果如图 所示
cha138/Article/program/Java/hx/201311/26939
相关参考
/** *authorannegu *date */ annegu做了一个简单的Http多线程的下载程序来讨论一下多线程并发下载以及断点续传的问题 这个程序的功能就是可以分多个线程从目标
用Java实现断点续传(HTTP) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 断点续传的原理
/* **SiteFileFetchjava */ packageNetFox; importjavaio*; import*; publicclassSiteFileFetc
用Java实现HTTP断点续传功能(3) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! /*
用Java实现HTTP断点续传功能(2) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! //启动
PHP下载远程文件类(支持断点续传) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! PHP下载远
如何使用.NET实现断点续传功能 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 顾名思义断点续传
使用VisualC#实现断点续传 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &nbs
DataGrid使用心得(附大量代码) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 为Data
HTTP/Apache错误代码汇总 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!最近测试defla