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

来源:互联网 发布:centos lamp环境搭建 编辑:程序博客网 时间:2024/05/01 12:06
本文所要讲的是Android断点续传的内容,以实例的形式进行了详细介绍。

一、断点续传的原理

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

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

       假设服务器域名为www.jizhuomi.com/android,文件名为down.zip。

get /down.zip http/1.1accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, application/vnd.ms-powerpoint, */*accept-language: zh-cnaccept-encoding: gzip, deflateuser-agent: mozilla/4.0 (compatible; msie 5.01; windows nt 5.0)connection: keep-alive

       服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:

200content-length=106786028accept-ranges=bytesdate=mon, 30 apr 2001 12:56:11 gmtetag=w/"02ca57e173c11:95b"content-type=application/octet-streamserver=microsoft-iis/5.0last-modified=mon, 30 apr 2001 12:56:11 gmt


       所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给web服务器的时候要多加一条信息--从哪里开始。

       下面是用自己编的一个“浏览器”来传递请求信息给web服务器,要求从2000070字节开始。

get /down.zip http/1.0user-agent: netfoxrange: bytes=2000070-accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

       仔细看一下就会发现多了一行 range: bytes=2000070-

       这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。

       服务器收到这个请求以后,返回的信息如下:

206content-length=106786028content-range=bytes 2000070-106786027/106786028date=mon, 30 apr 2001 12:55:20 gmtetag=w/"02ca57e173c11:95b"content-type=application/octet-streamserver=microsoft-iis/5.0last-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();   }   }   


0 0
原创粉丝点击