java实现断点续传
(一)断点续传的原理
其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已。
打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:
假设服务器域名为wwww.sjtu.edu,文件名为down.zip。
GET /down.zip HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
Excel, application/msWord, application/vnd.ms-Powerpoint, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Connection: Keep-Alive
服务器收到请求后,按要求寻请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:
其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已。
打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:
假设服务器域名为wwww.sjtu.edu,文件名为down.zip。
GET /down.zip HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
Excel, application/msWord, application/vnd.ms-Powerpoint, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Connection: Keep-Alive
服务器收到请求后,按要求寻请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:
200
Content-Length=106786028
Accept-Ranges=bytes
Date=Mon, 30 Apr 2001 12:56:11 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT
所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给
Web服务器的时候要多加一条信息--从哪里开始。
下面是用自己编的一个"浏览器"来传递请求信息给Web服务器,要求从2000070字节开始。
GET /down.zip HTTP/1.0
User-Agent: NetFox
RANGE: bytes=2000070-
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
仔细看一下就会发现多了一行RANGE: bytes=2000070-
这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。
服务器收到这个请求以后,返回的信息如下:
206
Content-Length=106786028
Content-Range=bytes 2000070-106786027/106786028
Date=Mon, 30 Apr 2001 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
仔细看一下就会发现多了一行RANGE: bytes=2000070-
这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。
服务器收到这个请求以后,返回的信息如下:
206
Content-Length=106786028
Content-Range=bytes 2000070-106786027/106786028
Date=Mon, 30 Apr 2001 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
和前面服务器返回的信息比较一下,就会发现增加了一行:
Content-Range=bytes 2000070-106786027/106786028
返回的代码也改为206了,而不再是200了。
知道了以上原理,就可以进行断点续传的编程了。
(二)Java实现断点续传的关键几点
用什么方法实现提交RANGE: bytes=2000070-。
当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Java的net包中提供了这种功能。代码如下:
URL url = new URL("www.sjtu.edu/down.zip");
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
//设置User-Agent
和前面服务器返回的信息比较一下,就会发现增加了一行:
Content-Range=bytes 2000070-106786027/106786028
返回的代码也改为206了,而不再是200了。
知道了以上原理,就可以进行断点续传的编程了。
(二)Java实现断点续传的关键几点
用什么方法实现提交RANGE: bytes=2000070-。
当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Java的net包中提供了这种功能。代码如下:
URL url = new URL("www.sjtu.edu/down.zip");
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
//设置User-Agent
httpConnection.setRequestProperty("User-Agent","NetFox");
//设置断点续传的开始位置
httpConnection.setRequestProperty("RANGE","bytes=2000070");
//获得输入流
InputStream input = InputStream();
从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。
大家看,其实断点续传用Java实现起来还是很简单的吧。
接下来要做的事就是怎么保存获得的流到文件中去了。
保存文件采用的方法。
我采用的是IO包中的RandAccessFile类。
操作相当简单,假设从2000070处开始保存文件,代码如下:
RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw");
long nPos = 2000070;
/
//设置断点续传的开始位置
httpConnection.setRequestProperty("RANGE","bytes=2000070");
//获得输入流
InputStream input = InputStream();
从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。
大家看,其实断点续传用Java实现起来还是很简单的吧。
接下来要做的事就是怎么保存获得的流到文件中去了。
保存文件采用的方法。
我采用的是IO包中的RandAccessFile类。
操作相当简单,假设从2000070处开始保存文件,代码如下:
RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw");
long nPos = 2000070;
/
/定位文件指针到nPos位置
oSavedFile.seek(nPos);
byte[] b = new byte[1024];
int nRead;
//从输入流中读入字节流,然后写到文件中
while((ad(b,0,1024)) > 0){
oSavedFile.write(b,0,nRead);
}
怎么样,也很简单吧。
接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。
(三)断点续传内核的实现
主要用了6个类,包括一个测试类。
1: SiteFileFetch.Java - 负责整个文件的抓取,控制内部线程(FileSplitterFetch)。
oSavedFile.seek(nPos);
byte[] b = new byte[1024];
int nRead;
//从输入流中读入字节流,然后写到文件中
while((ad(b,0,1024)) > 0){
oSavedFile.write(b,0,nRead);
}
怎么样,也很简单吧。
接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。
(三)断点续传内核的实现
主要用了6个类,包括一个测试类。
1: SiteFileFetch.Java - 负责整个文件的抓取,控制内部线程(FileSplitterFetch)。
2: FileSplitterFetch.Java - 负责部分文件的抓取。
3: FileAccess.Java - 负责文件的存储。
4: SiteInfoBean.Java - 要抓取的文件的信息,如文件保存的目录,名字,抓取文件的URL等。
5: Utility.Java - 工具类,放一些简单的方法。
6: TestMethod.Java - 测试类。
4: SiteInfoBean.Java - 要抓取的文件的信息,如文件保存的目录,名字,抓取文件的URL等。
5: Utility.Java - 工具类,放一些简单的方法。
6: TestMethod.Java - 测试类。
首先创建继承Thread类的传输文件线程类,其JAVA文件名为SiteFileFetch.java,代码如下:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.HttpURLConnection;
import java.URL;
/**
* 传输文件线程类。
* @author zhang
*/
public class SiteFileFetch extends Thread {
SiteInfoBean siteInfoBean = null;
/* 文件位置指针 */
long[] nPos;
/* 开始位置 */
long[] nStartPos;
/* 结束位置 */
long[] nEndPos;
/* 子线程对象 */
FileSplitterFetch[] fileSplitterFetch;
/* 文件长度 */
long nFileLength;
/* 是否第一次读取 */
boolean bFirst = true;
/* 停止标志 */
boolean bStop = false;
/* 文件传输临时信息 */
File tmpFile; //
/* 输出到文件的输出流 */
DataOutputStream output;
public SiteFileFetch(SiteInfoBean bean) throws IOException {
siteInfoBean = bean;
tmpFile = new SFilePath() + File.separator + SFileName() + ".info");
if (tmpFile.exists()) {
bFirst = false;
read_nPos();
} else {
nStartPos = new long[NSplitter()];
nEndPos = new long[NSplitter()];
}
}
public void run() {
try {
if (bFirst) {
java创建文件// 获得文件长度
nFileLength = getFileSize();
if (nFileLength == -1) {
System.err.println("File Length is not known");
} else if (nFileLength == -2) {
System.err.println("File is not access!");
} else {
// 分割下载文件
for (int i = 0; i < nStartPos.length; i++) {
nStartPos[i] = (long) (i * (nFileLength / nStartPos.length));
}
for (int i = 0; i < nEndPos.length - 1; i++) {
nEndPos[i] = nStartPos[i + 1];
}
nEndPos[nEndPos.length - 1] = nFileLength;
}
}
// 创建FileSplitterFetch类实例
fileSplitterFetch = new FileSplitterFetch[nStartPos.length];
// 启动FileSplitterFetch线程
for (int i = 0; i < nStartPos.length; i++) {
fileSplitterFetch[i] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),nStartPos[i], nEndPos[i], i);
Utility.log("Thread " + i + ",nStartPos=" + nStartPos[i] + " ,nEndPos= " + nEndPos[i]);
fileSplitterFetch[i].start();
}
boolean breakWhile = false;
// 等待子线程结束
while (!bStop) {
write_nPos();
Utility.sleep(500);
breakWhile = true;
for (int i = 0; i < nStartPos.length; i++) {
// 等待子线程返回
if (!fileSplitterFetch[i].bDownOver) {
breakWhile = false;
break;
}
}
// 是否结束while循环
if (breakWhile)
break;
}
System.out.println("文件传输结束!");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获得文件长度
* @return
*/
public long getFileSize() {
int nFileLength = -1;
try {
// 创建与WEB服务器的连接
URL url = new URL(siteInfoBean.getSSiteURL());
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setRequestProperty("User-Agent", "sumebrokentransfer");
int responseCode = ResponseCode();
if (responseCode >= 400) {
processErrorCode(responseCode);
// -2为WEB服务器响应错误
return -2;
}
String sHeader;
for (int i = 1;; i++) {
sHeader = HeaderFieldKey(i);
if (sHeader != null) {
if (sHeader.equals("Content-Length")) {
nFileLength = Integer.parseInt(HeaderField(sHeader));
break;
}
} else {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Utility.log(nFileLength);
return nFileLength;
}
/**
* 保存传输文件指针位置
*/
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论