java实现断点续传.docx
- 文档编号:10197020
- 上传时间:2023-02-09
- 格式:DOCX
- 页数:20
- 大小:21.48KB
java实现断点续传.docx
《java实现断点续传.docx》由会员分享,可在线阅读,更多相关《java实现断点续传.docx(20页珍藏版)》请在冰豆网上搜索。
java实现断点续传
java实现断点续传
(一)断点续传的原理
其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已。
打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:
假设服务器域名为,文件名为down.zip。
GET/down.zipHTTP/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;MSIE5.01;WindowsNT5.0)
Connection:
Keep-Alive
服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:
200
Content-Length=106786028
Accept-Ranges=bytes
Date=Mon,30Apr200112:
56:
11GMT
ETag=W/"02ca57e173c11:
95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon,30Apr200112:
56:
11GMT
所谓断点续传,也就是要从文件已经下载的地方开始继续下载。
所以在客户端浏览器传给
Web服务器的时候要多加一条信息--从哪里开始。
下面是用自己编的一个"浏览器"来传递请求信息给Web服务器,要求从2000070字节开始。
GET/down.zipHTTP/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=bytes2000070-106786027/106786028
Date=Mon,30Apr200112:
55:
20GMT
ETag=W/"02ca57e173c11:
95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon,30Apr200112:
55:
20GMT
和前面服务器返回的信息比较一下,就会发现增加了一行:
Content-Range=bytes2000070-106786027/106786028
返回的代码也改为206了,而不再是200了。
知道了以上原理,就可以进行断点续传的编程了。
(二)Java实现断点续传的关键几点
用什么方法实现提交RANGE:
bytes=2000070-。
当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Java的net包中提供了这种功能。
代码如下:
URLurl=newURL("
HttpURLConnectionhttpConnection=(HttpURLConnection)url.openConnection();
//设置User-Agent
httpConnection.setRequestProperty("User-Agent","NetFox");
//设置断点续传的开始位置
httpConnection.setRequestProperty("RANGE","bytes=2000070");
//获得输入流
InputStreaminput=httpConnection.getInputStream();
从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。
大家看,其实断点续传用Java实现起来还是很简单的吧。
接下来要做的事就是怎么保存获得的流到文件中去了。
保存文件采用的方法。
我采用的是IO包中的RandAccessFile类。
操作相当简单,假设从2000070处开始保存文件,代码如下:
RandomAccessoSavedFile=newRandomAccessFile("down.zip","rw");
longnPos=2000070;
//定位文件指针到nPos位置
oSavedFile.seek(nPos);
byte[]b=newbyte[1024];
intnRead;
//从输入流中读入字节流,然后写到文件中
while((nRead=input.read(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-测试类。
首先创建继承Thread类的传输文件线程类,其JAVA文件名为SiteFileFetch.java,代码如下:
importjava.io.DataInputStream;
importjava.io.DataOutputStream;
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.FileOutputStream;
importjava.io.IOException;
import.HttpURLConnection;
import.URL;
/**
*传输文件线程类。
*@authorzhang
*/
publicclassSiteFileFetchextendsThread{
SiteInfoBeansiteInfoBean=null;
/*文件位置指针*/
long[]nPos;
/*开始位置*/
long[]nStartPos;
/*结束位置*/
long[]nEndPos;
/*子线程对象*/
FileSplitterFetch[]fileSplitterFetch;
/*文件长度*/
longnFileLength;
/*是否第一次读取*/
booleanbFirst=true;
/*停止标志*/
booleanbStop=false;
/*文件传输临时信息*/
FiletmpFile;//
/*输出到文件的输出流*/
DataOutputStreamoutput;
publicSiteFileFetch(SiteInfoBeanbean)throwsIOException{
siteInfoBean=bean;
tmpFile=newFile(bean.getSFilePath()+File.separator+bean.getSFileName()+".info");
if(tmpFile.exists()){
bFirst=false;
read_nPos();
}else{
nStartPos=newlong[bean.getNSplitter()];
nEndPos=newlong[bean.getNSplitter()];
}
}
publicvoidrun(){
try{
if(bFirst){
//获得文件长度
nFileLength=getFileSize();
if(nFileLength==-1){
System.err.println("FileLengthisnotknown");
}elseif(nFileLength==-2){
System.err.println("Fileisnotaccess!
");
}else{
//分割下载文件
for(inti=0;i nStartPos[i]=(long)(i*(nFileLength/nStartPos.length)); } for(inti=0;i nEndPos[i]=nStartPos[i+1]; } nEndPos[nEndPos.length-1]=nFileLength; } } //创建FileSplitterFetch类实例 fileSplitterFetch=newFileSplitterFetch[nStartPos.length]; //启动FileSplitterFetch线程 for(inti=0;i fileSplitterFetch[i]=newFileSplitterFetch(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(); } booleanbreakWhile=false; //等待子线程结束 while(! bStop){ write_nPos(); Utility.sleep(500); breakWhile=true; for(inti=0;i //等待子线程返回 if(! fileSplitterFetch[i].bDownOver){ breakWhile=false; break; } } //是否结束while循环 if(breakWhile) break; } System.out.println("文件传输结束! "); }catch(Exceptione){ e.printStackTrace(); } } /** *获得文件长度 *@return */ publiclonggetFileSize(){ intnFileLength=-1; try{ //创建与WEB服务器的连接 URLurl=newURL(siteInfoBean.getSSiteURL()); HttpURLConnectionhttpConnection=(HttpURLConnection)url.openConnection(); httpConnection.setRequestProperty("User-Agent","sample.resumebrokentransfer"); intresponseCode=httpConnection.getResponseCode(); if(responseCode>=400){ processErrorCode(responseCode); //-2为WEB服务器响应错误 return-2; } StringsHeader; for(inti=1;;i++){ sHeader=httpConnection.getHeaderFieldKey(i); if(sHeader! =null){ if(sHeader.equals("Content-Length")){ nFileLength=Integer.parseInt(httpConnection.getHeaderField(sHeader)); break; } }else{ break; } } }catch(IOExceptione){ e.printStackTrace(); }catch(Exceptione){ e.printStackTrace(); } Utility.log(nFileLength); returnnFileLength; } /** *保存传输文件指针位置 */ privatevoidwrite_nPos(){ try{ output=newDataOutputStream(newFileOutputStream(tmpFile)); output.writeInt(nStartPos.length); for(inti=0;i output.writeLong(fileSplitterFetch[i].nStartPos); output.writeLong(fileSplitterFetch[i].nEndPos); } output.close(); }catch(IOExceptione){ e.printStackTrace(); }catch(Exceptione){ e.printStackTrace(); } } /** *读取保存的下载文件指针位置 */ privatevoidread_nPos(){ try{ DataInputStreaminput=newDataInputStream(newFileInputStream(tmpFile)); intnCount=input.readInt(); nStartPos=newlong[nCount]; nEndPos=newlong[nCount]; for(inti=0;i nStartPos[i]=input.readLong(); nEndPos[i]=input.readLong(); } input.close(); }catch(IOExceptione){ e.printStackTrace(); }catch(Exceptione){ e.printStackTrace(); } } privatevoidprocessErrorCode(intnErrorCode){ System.err.println("ErrorCode: "+nErrorCode); } publicvoidsiteStop(){ bStop=true; for(inti=0;i fileSplitterFetch[i].splitterStop(); } } 创建继承Thread类的将要传输的网络文件分割线程类,文件名为FileSplitterFetch.java importjava.io.IOException; importjava.io.InputStream; import.HttpURLConnection; import.URL; publicclassFileSplitterFetchextendsThread{ /*定义文件传输时使用的变量*/ StringsURL; /*分段文件传输开始位置*/ longnStartPos; /*分段文件传输结束位置*/ longnEndPos; /*子线程ID*/ intnThreadID; /*完成文件传输*/ booleanbDownOver=false; /*停止文件传输*/ booleanbStop=false; FileAccessfileAccess=null; /** * *@paramsURL *@paramsName *@paramnStart *@paramnEnd *@paramid *@throwsIOException */ publicFileSplitterFetch(StringsURL,StringsName,longnStart,longnEnd,intid)throwsIOException{ this.sURL=sURL; this.nStartPos=nStart; this.nEndPos=nEnd; nThreadID=id; //创建文件并打开 fileAccess=newFileAccess(sName,nStartPos); } /** * */ publicvoidrun(){ while(nStartPos bStop){ //创建连接 try{ URLurl=newURL(sURL); HttpURLConnectionhttpConnection=(HttpURLConnection)url.openConnection(); httpConnection.setRequestProperty("User-Agent","NextFox"); StringsProperty="bytes="+nStartPos+"-"; httpConnection.setRequestProperty("RANGE",sProperty); Utility.log(sProperty); //创建输入流对象 InputStreaminput=httpConnection.getInputStream(); byte[]b=newbyte[1024]; intnRead; while((nRead=input.read(b,0,1024))>0&&nStartPos bStop){ nStartPos+=fileAccess.write(b,0,nRead); } Utility.log("Thread"+nThreadID+"isover! "); bDownOver=true; }catch(Exceptione){ e.printStackTrace(); } } } /** *处理和响应服务器头数据。 *@paramcon */ publicvoidlogResponseHead(HttpURLConnectioncon){ for(inti=1;;i++){ Stringheader=con.getHeaderFieldKey(i); if(header! =null){ Utility.log(header+": "+con.getHeaderField(header)); }else{ break; } } } publicvoidsplitterStop(){ bStop=true; } } 创建设置和获取网络信息类,类名为SiteInfoBean.java /** *定义获取和设置相关文件类信息。 *@authorzhang */ publicclassSiteInfoBean{ /*定义URL变量*/ privateStringsSiteURL; /*定义存文件路径变量*/ privateStringsFilePath; /*定义文件名变量*/ privateStringsFileName; /*定义传输文件计数器*/ privateintnSplitter; publicSiteInfoBean(){ this("","","",5); } publicSiteInf
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- java 实现断点续传 实现 断点续传