断点续传的原理剖析与实例讲解

本文所要讲的是Android断点续传的内容,以实例的形式进行了详细介绍。

一、断点续传的原理

       其实断点续传的原理很简单,就是在http的请求上和一般的下载有所不同而已。

       打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:

       假设服务器域名为www.jizhuomi.com/android,文件名为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实现断点续传的关键几点

       用什么方法实现提交range: bytes=2000070-?

       当然用最原始的socket是肯定能完成的,不过那样太费事了,其实java的net包中提供了这种功能。代码如下:

Java代码
url url = new url("http://www.jizhuomi.com/android/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处开始保存文件,代码如下:

Java代码
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测试类。

       四、实例源码

       下面是源程序:

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 = (long)(i*(nfilelength/nstartpos.length));   
}   
for(int i=0;i<nendpos.length-1;i++)   
{   
nendpos = nstartpos[i+1];   
}   
nendpos[nendpos.length-1] = nfilelength;   
}   
}   
//启动子线程   
filesplitterfetch = new filesplitterfetch[nstartpos.length];   
for(int i=0;i<nstartpos.length;i++)   
{   
filesplitterfetch = new filesplitterfetch(siteinfobean.getssiteurl(),   
siteinfobean.getsfilepath() + file.separator + siteinfobean.getsfilename(),   
nstartpos,nendpos,i);   
utility.log("thread " + i + " , nstartpos = " + nstartpos + ", nendpos = " + nendpos);   
filesplitterfetch.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.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);   
output.writelong(filesplitterfetch.nstartpos);   
output.writelong(filesplitterfetch.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 = input.readlong();   
nendpos = 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.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; //threads 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; //sites url   
private string sfilepath; //saved files path   
private string sfilename; //saved files 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、付费专栏及课程。

余额充值