java实现断点续传

其实原理很简单,只是在   http   请求中加一个文件的偏移量而已,当然这还需要   server   支持这个头才行。
手头上刚好有一篇这样的文档。 

(一)断点续传的原理 
其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已。 
打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 
假设服务器域名为wwww.sjtu.edu.cn,文件名为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 
Last-Modified=Mon,   30   Apr   2001   12:55:20   GMT 


和前面服务器返回的信息比较一下,就会发现增加了一行: 
Content-Range=bytes   2000070-106786027/106786028 
返回的代码也改为206了,而不再是200了。 


知道了以上原理,就可以进行断点续传的编程了。 


(二)Java实现断点续传的关键几点 


(1)用什么方法实现提交RANGE:   bytes=2000070-。 
当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Java的net包中提供了这种功能。代码如下: 
URL   url   =   new   URL( "http://www.sjtu.edu.cn/down.zip "); 
HttpURLConnection   httpConnection   =   (HttpURLConnection)url.openConnection 

  

(); 
//设置User-Agent 
httpConnection.setRequestProperty( "User-Agent ", "NetFox "); 
//设置断点续传的开始位置 
httpConnection.setRequestProperty( "RANGE ", "bytes=2000070 "); 
//获得输入流 
InputStream   input   =   httpConnection.getInputStream(); 


从输入流中取出的字节流就是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((nRead=input.read(b,0,1024))   >   0) 

oSavedFile.write(b,0,nRead); 


怎么样,也很简单吧。 
接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。 


(三)断点续传内核的实现 
主要用了6个类,包括一个测试类。 
SiteFileFetch.java负责整个文件的抓取,控制内部线程(FileSplitterFetch类)。 
FileSplitterFetch.java负责部分文件的抓取。 
FileAccess.java负责文件的存储。 
SiteInfoBean.java要抓取的文件的信息,如文件保存的目录,名字,抓取文件的URL等。 
Utility.java工具类,放一些简单的方法。 
TestMethod.java测试类。 


下面是源程序:   
/* 
**SiteFileFetch.java 
*/ 
package   NetFox; 
import   java.io.*; 
import   java.net.*; 


public   class   SiteFileFetch   extends   Thread   { 


SiteInfoBean   siteInfoBean   =   null;   //文件信息Bean 
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   =   File.createTempFile   ( "zhong ", "1111 ",new   File(bean.getSFilePath())); 
tmpFile   =   new   File(bean.getSFilePath()+File.separator   +   bean.getSFileName()+ ".info "); 
if(tmpFile.exists   ()) 

bFirst   =   false; 
read_nPos(); 

else 

nStartPos   =   new   long[bean.getNSplitter()]; 
nEndPos   =   new   long[bean.getNSplitter()]; 






public   void   run() 

//获得文件长度 
//分割文件 
//实例FileSplitterFetch 
//启动FileSplitterFetch线程 
//等待子线程返回 
try{ 
if(bFirst) 

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   =   new   FileSplitterFetch[nStartPos.length]; 
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(); 

//   fileSplitterFetch[nPos.length-1]   =   new   FileSplitterFetch(siteInfoBean.getSSiteURL(), 
siteInfoBean.getSFilePath()   +   File.separator   +   siteInfoBean.getSFileName(),nPos[nPos.length-1],nFileLength,nPos.length-1); 
//   Utility.log( "Thread   "   +   (nPos.length-1)   +   "   ,   nStartPos   =   "   +   nPos[nPos.length-1]   +   ", 
nEndPos   =   "   +   nFileLength); 
//   fileSplitterFetch[nPos.length-1].start(); 


//等待子线程结束 
//int   count   =   0; 
//是否结束while循环 
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; 


if(breakWhile) 
break; 


//count++; 
//if(count> 4) 
//   siteStop(); 



System.err.println( "文件下载结束! "); 

catch(Exception   e){e.printStackTrace   ();} 



//获得文件长度 
public   long   getFileSize() 

int   nFileLength   =   -1; 
try{ 
URL   url   =   new   URL(siteInfoBean.getSSiteURL()); 
HttpURLConnection   httpConnection   =   (HttpURLConnection)url.openConnection   (); 
httpConnection.setRequestProperty( "User-Agent ", "NetFox "); 


int   responseCode=httpConnection.getResponseCode(); 
if(responseCode> =400) 

processErrorCode(responseCode); 
return   -2;   //-2   represent   access   is   error 



String   sHeader; 


for(int   i=1;;i++) 

//DataInputStream   in   =   new   DataInputStream(httpConnection.getInputStream   ()); 
//Utility.log(in.readLine()); 
sHeader=httpConnection.getHeaderFieldKey(i); 
if(sHeader!=null) 

if(sHeader.equals( "Content-Length ")) 

nFileLength   =   Integer.parseInt(httpConnection.getHeaderField(sHeader)); 
break; 


else 
break; 


catch(IOException   e){e.printStackTrace   ();} 
catch(Exception   e){e.printStackTrace   ();} 


Utility.log(nFileLength); 


return   nFileLength; 



//保存下载信息(文件指针位置) 
private   void   write_nPos() 

try{ 
output   =   new   DataOutputStream(new   FileOutputStream(tmpFile)); 
output.writeInt(nStartPos.length); 
for(int   i=0;i <nStartPos.length;i++) 

//   output.writeLong(nPos[i]); 
output.writeLong(fileSplitterFetch[i].nStartPos); 
output.writeLong(fileSplitterFetch[i].nEndPos); 

output.close(); 

catch(IOException   e){e.printStackTrace   ();} 
catch(Exception   e){e.printStackTrace   ();} 



//读取保存的下载信息(文件指针位置) 
private   void   read_nPos() 

try{ 
DataInputStream   input   =   new   DataInputStream(new   FileInputStream(tmpFile)); 
int   nCount   =   input.readInt(); 
nStartPos   =   new   long[nCount]; 
nEndPos   =   new   long[nCount]; 
for(int   i=0;i <nStartPos.length;i++) 

nStartPos[i]   =   input.readLong(); 
nEndPos[i]   =   input.readLong(); 

input.close(); 

catch(IOException   e){e.printStackTrace   ();} 
catch(Exception   e){e.printStackTrace   ();} 



private   void   processErrorCode(int   nErrorCode) 

System.err.println( "Error   Code   :   "   +   nErrorCode); 



//停止文件下载 
public   void   siteStop() 

bStop   =   true; 
for(int   i=0;i <nStartPos.length;i++) 
fileSplitterFetch[i].splitterStop(); 




/* 
**FileSplitterFetch.java 
*/ 
package   NetFox; 


import   java.io.*; 
import   java.net.*; 


public   class   FileSplitterFetch   extends   Thread   { 


String   sURL;   //File   URL 
long   nStartPos;   //File   Snippet   Start   Position 
long   nEndPos;   //File   Snippet   End   Position 
int   nThreadID;   //Thread 's   ID 
boolean   bDownOver   =   false;   //Downing   is   over 
boolean   bStop   =   false;   //Stop   identical 
FileAccessI   fileAccessI   =   null;   //File   Access   interface 


public   FileSplitterFetch(String   sURL,String   sName,long   nStart,long   nEnd,int   id)   throws   IOException 

this.sURL   =   sURL; 
this.nStartPos   =   nStart; 
this.nEndPos   =   nEnd; 
nThreadID   =   id; 
fileAccessI   =   new   FileAccessI(sName,nStartPos); 



public   void   run() 

while(nStartPos   <   nEndPos   &&   !bStop) 



try{ 
URL   url   =   new   URL(sURL); 
HttpURLConnection   httpConnection   =   (HttpURLConnection)url.openConnection   (); 
httpConnection.setRequestProperty( "User-Agent ", "NetFox "); 
String   sProperty   =   "bytes= "+nStartPos+ "- "; 
httpConnection.setRequestProperty( "RANGE ",sProperty); 
Utility.log(sProperty); 


InputStream   input   =   httpConnection.getInputStream(); 
//logResponseHead(httpConnection); 


byte[]   b   =   new   byte[1024]; 
int   nRead; 
while((nRead=input.read(b,0,1024))   >   0   &&   nStartPos   <   nEndPos   &&   !bStop) 

nStartPos   +=   fileAccessI.write(b,0,nRead); 
//if(nThreadID   ==   1) 
//   Utility.log( "nStartPos   =   "   +   nStartPos   +   ",   nEndPos   =   "   +   nEndPos); 



Utility.log( "Thread   "   +   nThreadID   +   "   is   over! "); 
bDownOver   =   true; 
//nPos   =   fileAccessI.write   (b,0,nRead); 

catch(Exception   e){e.printStackTrace   ();} 




//打印回应的头信息 
public   void   logResponseHead(HttpURLConnection   con) 

for(int   i=1;;i++) 

String   header=con.getHeaderFieldKey(i); 
if(header!=null) 
//responseHeaders.put(header,httpConnection.getHeaderField(header)); 
Utility.log(header+ "   :   "+con.getHeaderField(header)); 
else 
break; 




public   void   splitterStop() 

bStop   =   true; 






/* 
**FileAccess.java 
*/ 
package   NetFox; 
import   java.io.*; 


public   class   FileAccessI   implements   Serializable{ 


RandomAccessFile   oSavedFile; 
long   nPos; 


public   FileAccessI()   throws   IOException 

this( " ",0); 



public   FileAccessI(String   sName,long   nPos)   throws   IOException 

oSavedFile   =   new   RandomAccessFile(sName, "rw "); 
this.nPos   =   nPos; 
oSavedFile.seek(nPos); 



public   synchronized   int   write(byte[]   b,int   nStart,int   nLen) 

int   n   =   -1; 
try{ 
oSavedFile.write(b,nStart,nLen); 
n   =   nLen; 

catch(IOException   e) 

e.printStackTrace   (); 



return   n; 






/* 
**SiteInfoBean.java 
*/ 
package   NetFox; 


public   class   SiteInfoBean   { 


private   String   sSiteURL;   //Site 's   URL 
private   String   sFilePath;   //Saved   File 's   Path 
private   String   sFileName;   //Saved   File 's   Name 
private   int   nSplitter;   //Count   of   Splited   Downloading   File 


public   SiteInfoBean() 

//default   value   of   nSplitter   is   5 
this( " ", " ", " ",5); 



public   SiteInfoBean(String   sURL,String   sPath,String   sName,int   nSpiltter) 

sSiteURL=   sURL; 
sFilePath   =   sPath; 
sFileName   =   sName; 
this.nSplitter   =   nSpiltter; 





public   String   getSSiteURL() 

return   sSiteURL; 



public   void   setSSiteURL(String   value) 

sSiteURL   =   value; 



public   String   getSFilePath() 

return   sFilePath; 



public   void   setSFilePath(String   value) 

sFilePath   =   value; 



public   String   getSFileName() 

return   sFileName; 



public   void   setSFileName(String   value) 

sFileName   =   value; 



public   int   getNSplitter() 

return   nSplitter; 



public   void   setNSplitter(int   nCount) 

nSplitter   =   nCount; 




/* 
**Utility.java 
*/ 
package   NetFox; 


public   class   Utility   { 


public   Utility() 






public   static   void   sleep(int   nSecond) 

try{ 
Thread.sleep(nSecond); 

catch(Exception   e) 

e.printStackTrace   (); 




public   static   void   log(String   sMsg) 

System.err.println(sMsg); 



public   static   void   log(int   sMsg) 

System.err.println(sMsg); 




/* 
**TestMethod.java 
*/ 
package   NetFox; 


public   class   TestMethod   { 


public   TestMethod() 
{   ///xx/weblogic60b2_win.exe 
try{ 
SiteInfoBean   bean   =   new   SiteInfoBean( "http://localhost/xx/weblogic60b2_win.exe ", "L:\\temp ", "weblogic60b2_win.exe ",5); 
//SiteInfoBean   bean   =   new   SiteInfoBean( "http://localhost:8080/down.zip ", "L:\\temp ", "weblogic60b2_win.exe ",5); 
SiteFileFetch   fileFetch   =   new   SiteFileFetch(bean); 
fileFetch.start(); 

catch(Exception   e){e.printStackTrace   ();} 





public   static   void   main(String[]   args) 

new   TestMethod(); 



  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值