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

本文所要讲的是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(); } }

以上就是对Android 断点续传的资料整理,后续继续补充相关资料,谢谢大家对本站的支持!

时间: 2024-10-27 07:27:11

Android 断点续传的原理剖析与实例讲解的相关文章

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

 本文所要讲的是Android断点续传的内容,以实例的形式进行了详细介绍.        一.断点续传的原理        其实断点续传的原理很简单,就是在http的请求上和一般的下载有所不同而已.        打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:        假设服务器域名为www.jizhuomi.com/android,文件名为down.zip. get /down.zip http/1.1 accept: image/gif, image/x-xbitmap,

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

http://www.jizhuomi.com/android/example/342.html

Android 常见的四种对话框实例讲解_Android

1.对话框通知(Dialog Notification) 当你的应用需要显示一个进度条或需要用户对信息进行确认时,可以使用对话框来完成. 下面代码将打开一个如图所示的对话框: public void click1(View view) { AlertDialog.Builder builder = new Builder(this); builder.setTitle("工学1号馆"); builder.setIcon(R.drawable.ic_launcher); builder.

Android 常见的四种对话框实例讲解

1.对话框通知(Dialog Notification) 当你的应用需要显示一个进度条或需要用户对信息进行确认时,可以使用对话框来完成. 下面代码将打开一个如图所示的对话框: public void click1(View view) { AlertDialog.Builder builder = new Builder(this); builder.setTitle("工学1号馆"); builder.setIcon(R.drawable.ic_launcher); builder.

《深入理解Android:Telephony原理剖析与最佳实践》一3.5 本章小结

3.5 本章小结 本章主要对Android Telephony相关模块使用到的一些关键技术进行了分析和实例讲解,包括Handler.Broadcast两种异步通信的机制和AIDL跨应用的服务调用.Handler主要有几种消息处理方式?Handler对象的创建在主线程和子线程有什么区别?AIDL如何定义接口?AIDL接口定义完成后,对应的代码是如何生成的?Broadcast接收方的过滤器有几种设置方式?通过本章的学习,相信大家都对这些知识点会有较深入的认识和理解. 对Android中的Handle

《深入理解Android:Telephony原理剖析与最佳实践》一1.2 Android系统架构

1.2 Android系统架构 前面学习了智能手机的基本硬件结构,可以通过功能手机与智能手机的特点和区别从本质上认识它们.Android智能手机操作系统作为运行在AP上的开源智能手机操作系统,其系统架构是什么样的呢?我们先看看图1-2. 通过图1-2不难发现,Android手机操作系统是一个基于Linux Kernel的分层智能手机操作系统,其共分为4层,从上到下分别是Java Applications(应用层).Java Frameworks(应用框架层).User Libraries(系统运

《深入理解Android:Telephony原理剖析与最佳实践》一导读

前 言 随着ICT技术的推广和发展,在三网融合.智慧的地球(物联网)等概念风行的形势下,手机终端技术的发展和进步可谓日新月异:Android经受住了市场的考验,并占据了较大的市场份额,从手机芯片厂家.手机生产厂家到各种应用开发公司.互联网公司等,逐步形成了以Android系统平台为核心的上下游产业链.正是由于Android源代码的开源,各大手机厂商才能通过深度定制Android系统平台,在降低研发成本的同时,快速推出具有自身特色的Android智能手机产品.因此,各大手机厂家在推出新版本Andr

《深入理解Android:Telephony原理剖析与最佳实践》一3.2 Handler消息处理机制

3.2 Handler消息处理机制 Android在设计的时候引入了Handler消息机制,每一个消息发送到主线程的消息队列中,消息队列遵循先进先出原则,发送消息并不会阻塞线程,而接收线程会阻塞线程.我们接着来看看在Android中对Handler是如何定义的? 3.2.1 Handler概念分析 Handler的官方定义如下: A Handler allows you to send and process Message and Runnable objects associated wit

Android判断用户的网络类型实例讲解(2/3/4G、wifi)_Android

很多时候需要先判断当前用户的网络,才会继续之后的一些处理逻辑.但网络类型获取这一块,我用我自己的的手机调试时遇到一些问题,这里记录一下. 一加手机一代,移动4G 网络,得到的subtype类型值为17,我查过Android 5.1的源码,它最大的值也就为16.  我拿魅族的移动4G测试的结果如下: 小米4,电信4G的测试结果如下: 魅族MX4,联通3G 还测试了其它华为移动3G/4G的情况,就我自己的手机一加返回的值有点奇怪,之后我查了一下它的参数: 当然,其它厂商:华为.小米.魅族与上面显示的