本文所要讲的是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代码
  1. url url = new url("http://www.jizhuomi.com/android/down.zip");   
  2. httpurlconnection httpconnection = (httpurlconnection)url.openconnection();   
  3. //设置user-agent   
  4. httpconnection.setrequestproperty("user-agent","netfox");   
  5. //设置断点续传的开始位置   
  6. httpconnection.setrequestproperty("range","bytes=2000070");   
  7. //获得输入流   
  8. inputstream input = httpconnection.getinputstream();  

       从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。

       大家看,其实断点续传用java实现起来还是很简单的吧。

       接下来要做的事就是怎么保存获得的流到文件中去了。

       保存文件采用的方法:我采用的是io包中的randaccessfile类

       操作相当简单,假设从2000070处开始保存文件,代码如下:

Java代码
  1. randomaccess osavedfile = new randomaccessfile("down.zip","rw");   
  2. long npos = 2000070;   
  3. //定位文件指针到npos位置   
  4. osavedfile.seek(npos);   
  5. byte[] b = new byte[1024];   
  6. int nread;   
  7. //从输入流中读入字节流,然后写到文件中   
  8. while((nread=input.read(b,0,1024)) > 0)   
  9. {   
  10.     osavedfile.write(b,0,nread);   
  11. }  

       怎么样,也很简单吧。

       接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。

       三、断点续传内核的实现

       主要用了6个类,包括一个测试类。

       sitefilefetch.java负责整个文件的抓取,控制内部线程(filesplitterfetch类)。

       filesplitterfetch.java负责部分文件的抓取。

       fileaccess.java负责文件的存储。

       siteinfobean.java要抓取的文件的信息,如文件保存的目录,名字,抓取文件的url等。

       utility.java工具类,放一些简单的方法。

       testmethod.java测试类。

       四、实例源码

       下面是源程序:

Java代码
  1. /*  
  2. **sitefilefetch.java  
  3. */  
  4. package netfox;   
  5. import java.io.*;   
  6. import java.net.*;   
  7.    
  8. public class sitefilefetch extends thread {   
  9.     
  10. siteinfobean siteinfobean = null//文件信息bean   
  11. long[] nstartpos; //开始位置   
  12. long[] nendpos; //结束位置   
  13. filesplitterfetch[] filesplitterfetch; //子线程对象   
  14. long nfilelength; //文件长度   
  15. boolean bfirst = true//是否第一次取文件   
  16. boolean bstop = false//停止标志   
  17. file tmpfile; //文件下载的临时信息   
  18. dataoutputstream output; //输出到文件的输出流    
  19.   
  20. public sitefilefetch(siteinfobean bean) throws ioexception   
  21. {   
  22. siteinfobean = bean;   
  23. //tmpfile = file.createtempfile ("zhong","1111",new file(bean.getsfilepath()));   
  24. tmpfile = new file(bean.getsfilepath()+file.separator + bean.getsfilename()+".info");   
  25. if(tmpfile.exists ())   
  26. {   
  27. bfirst = false;   
  28. read_npos();   
  29. }   
  30. else  
  31. {   
  32. nstartpos = new long[bean.getnsplitter()];   
  33. nendpos = new long[bean.getnsplitter()];   
  34. }      
  35. }   
  36.     
  37. public void run()   
  38. {   
  39. //获得文件长度   
  40. //分割文件   
  41. //实例filesplitterfetch   
  42. //启动filesplitterfetch线程   
  43. //等待子线程返回   
  44. try{   
  45. if(bfirst)   
  46. {   
  47. nfilelength = getfilesize();   
  48. if(nfilelength == -1)   
  49. {   
  50. system.err.println("file length is not known!");   
  51. }   
  52. else if(nfilelength == -2)   
  53. {   
  54. system.err.println("file is not access!");   
  55. }   
  56. else  
  57. {   
  58. for(int i=0;i<nstartpos.length;i++)   
  59. {   
  60. nstartpos = (long)(i*(nfilelength/nstartpos.length));   
  61. }   
  62. for(int i=0;i<nendpos.length-1;i++)   
  63. {   
  64. nendpos = nstartpos[i+1];   
  65. }   
  66. nendpos[nendpos.length-1] = nfilelength;   
  67. }   
  68. }   
  69. //启动子线程   
  70. filesplitterfetch = new filesplitterfetch[nstartpos.length];   
  71. for(int i=0;i<nstartpos.length;i++)   
  72. {   
  73. filesplitterfetch = new filesplitterfetch(siteinfobean.getssiteurl(),   
  74. siteinfobean.getsfilepath() + file.separator + siteinfobean.getsfilename(),   
  75. nstartpos,nendpos,i);   
  76. utility.log("thread " + i + " , nstartpos = " + nstartpos + ", nendpos = " + nendpos);   
  77. filesplitterfetch.start();   
  78. }   
  79. // filesplitterfetch[npos.length-1] = new filesplitterfetch(siteinfobean.getssiteurl(),   
  80. siteinfobean.getsfilepath() + file.separator + siteinfobean.getsfilename(),npos[npos.length-1],nfilelength,npos.length-1);   
  81. // utility.log("thread " + (npos.length-1) + " , nstartpos = " + npos[npos.length-1] + ",   
  82. nendpos = " + nfilelength);   
  83. // filesplitterfetch[npos.length-1].start();   
  84.   
  85. //等待子线程结束   
  86. //int count = 0;   
  87. //是否结束while循环   
  88. boolean breakwhile = false;   
  89.     
  90. while(!bstop)   
  91. {   
  92. write_npos();   
  93. utility.sleep(500);   
  94. breakwhile = true;   
  95.     
  96. for(int i=0;i<nstartpos.length;i++)   
  97. {   
  98. if(!filesplitterfetch.bdownover)   
  99. {   
  100. breakwhile = false;   
  101. break;   
  102. }   
  103. }   
  104. if(breakwhile)   
  105. break;   
  106.    
  107. //count++;   
  108. //if(count>4)   
  109. // sitestop();   
  110. }   
  111.    
  112. system.err.println("文件下载结束!");   
  113. }   
  114. catch(exception e){e.printstacktrace ();}   
  115. }   
  116.    
  117. //获得文件长度   
  118. public long getfilesize()   
  119. {   
  120. int nfilelength = -1;   
  121. try{   
  122. url url = new url(siteinfobean.getssiteurl());   
  123. httpurlconnection httpconnection = (httpurlconnection)url.openconnection ();   
  124. httpconnection.setrequestproperty("user-agent","netfox");    
  125.   
  126. int responsecode=httpconnection.getresponsecode();   
  127. if(responsecode>=400)   
  128. {   
  129. processerrorcode(responsecode);   
  130. return -2//-2 represent access is error   
  131. }   
  132.    
  133. string sheader;   
  134.     
  135. for(int i=1;;i++)   
  136. {   
  137. //datainputstream in = new datainputstream(httpconnection.getinputstream ());   
  138. //utility.log(in.readline());   
  139. sheader=httpconnection.getheaderfieldkey(i);   
  140. if(sheader!=null)   
  141. {   
  142. if(sheader.equals("content-length"))   
  143. {   
  144. nfilelength = integer.parseint(httpconnection.getheaderfield(sheader));   
  145. break;   
  146. }   
  147. }   
  148. else  
  149. break;   
  150. }   
  151. }   
  152. catch(ioexception e){e.printstacktrace ();}   
  153. catch(exception e){e.printstacktrace ();}   
  154.    
  155. utility.log(nfilelength);   
  156.    
  157. return nfilelength;   
  158. }   
  159.     
  160. //保存下载信息(文件指针位置)   
  161. private void write_npos()   
  162. {   
  163. try{   
  164. output = new dataoutputstream(new fileoutputstream(tmpfile));   
  165. output.writeint(nstartpos.length);   
  166. for(int i=0;i<nstartpos.length;i++)   
  167. {   
  168. // output.writelong(npos);   
  169. output.writelong(filesplitterfetch.nstartpos);   
  170. output.writelong(filesplitterfetch.nendpos);   
  171. }   
  172. output.close();   
  173. }   
  174. catch(ioexception e){e.printstacktrace ();}   
  175. catch(exception e){e.printstacktrace ();}   
  176. }   
  177.     
  178. //读取保存的下载信息(文件指针位置)   
  179. private void read_npos()   
  180. {   
  181. try{   
  182. datainputstream input = new datainputstream(new fileinputstream(tmpfile));   
  183. int ncount = input.readint();   
  184. nstartpos = new long[ncount];   
  185. nendpos = new long[ncount];   
  186. for(int i=0;i<nstartpos.length;i++)   
  187. {   
  188. nstartpos = input.readlong();   
  189. nendpos = input.readlong();   
  190. }   
  191. input.close();   
  192. }   
  193. catch(ioexception e){e.printstacktrace ();}   
  194. catch(exception e){e.printstacktrace ();}   
  195. }   
  196.    
  197. private void processerrorcode(int nerrorcode)   
  198. {   
  199. system.err.println("error code : " + nerrorcode);   
  200. }   
  201.     
  202. //停止文件下载   
  203. public void sitestop()   
  204. {   
  205. bstop = true;   
  206. for(int i=0;i<nstartpos.length;i++)   
  207. filesplitterfetch.splitterstop();    
  208. }   
  209. }   
  210. /*  
  211. **filesplitterfetch.java  
  212. */  
  213. package netfox;   
  214.     
  215. import java.io.*;   
  216. import java.net.*;   
  217.    
  218. public class filesplitterfetch extends thread {   
  219.     
  220. string surl; //file url   
  221. long nstartpos; //file snippet start position   
  222. long nendpos; //file snippet end position   
  223. int nthreadid; //threads id   
  224. boolean bdownover = false//downing is over   
  225. boolean bstop = false//stop identical   
  226. fileaccessi fileaccessi = null//file access interface   
  227.     
  228. public filesplitterfetch(string surl,string sname,long nstart,long nend,int id) throws ioexception   
  229. {   
  230. this.surl = surl;   
  231. this.nstartpos = nstart;   
  232. this.nendpos = nend;   
  233. nthreadid = id;   
  234. fileaccessi = new fileaccessi(sname,nstartpos);   
  235. }   
  236.     
  237. public void run()   
  238. {   
  239. while(nstartpos < nendpos && !bstop)   
  240. {     
  241. try{   
  242. url url = new url(surl);   
  243. httpurlconnection httpconnection = (httpurlconnection)url.openconnection ();   
  244. httpconnection.setrequestproperty("user-agent","netfox");   
  245. string sproperty = "bytes="+nstartpos+"-";   
  246. httpconnection.setrequestproperty("range",sproperty);   
  247. utility.log(sproperty);   
  248.     
  249. inputstream input = httpconnection.getinputstream();   
  250. //logresponsehead(httpconnection);   
  251.    
  252. byte[] b = new byte[1024];   
  253. int nread;   
  254. while((nread=input.read(b,0,1024)) > 0 && nstartpos < nendpos && !bstop)   
  255. {   
  256. nstartpos += fileaccessi.write(b,0,nread);   
  257. //if(nthreadid == 1)   
  258. // utility.log("nstartpos = " + nstartpos + ", nendpos = " + nendpos);   
  259. }   
  260.     
  261. utility.log("thread " + nthreadid + " is over!");   
  262. bdownover = true;   
  263. //npos = fileaccessi.write (b,0,nread);   
  264. }   
  265. catch(exception e){e.printstacktrace ();}   
  266. }   
  267. }   
  268.    
  269. //打印回应的头信息   
  270. public void logresponsehead(httpurlconnection con)   
  271. {   
  272. for(int i=1;;i++)   
  273. {   
  274. string header=con.getheaderfieldkey(i);   
  275. if(header!=null)   
  276. //responseheaders.put(header,httpconnection.getheaderfield(header));   
  277. utility.log(header+" : "+con.getheaderfield(header));   
  278. else  
  279. break;   
  280. }   
  281. }   
  282.     
  283. public void splitterstop()   
  284. {   
  285. bstop = true;   
  286. }      
  287. }   
  288.     
  289. /*  
  290. **fileaccess.java  
  291. */  
  292. package netfox;   
  293. import java.io.*;   
  294.     
  295. public class fileaccessi implements serializable{   
  296.     
  297. randomaccessfile osavedfile;   
  298. long npos;   
  299.    
  300. public fileaccessi() throws ioexception   
  301. {   
  302. this("",0);   
  303. }   
  304.     
  305. public fileaccessi(string sname,long npos) throws ioexception   
  306. {   
  307. osavedfile = new randomaccessfile(sname,"rw");   
  308. this.npos = npos;   
  309. osavedfile.seek(npos);   
  310. }   
  311.     
  312. public synchronized int write(byte[] b,int nstart,int nlen)   
  313. {   
  314. int n = -1;   
  315. try{   
  316. osavedfile.write(b,nstart,nlen);   
  317. n = nlen;   
  318. }   
  319. catch(ioexception e)   
  320. {   
  321. e.printstacktrace ();   
  322. }   
  323.     
  324. return n;   
  325. }     
  326. }   
  327.     
  328. /*  
  329. **siteinfobean.java  
  330. */  
  331. package netfox;   
  332.     
  333. public class siteinfobean {   
  334.     
  335. private string ssiteurl; //sites url   
  336. private string sfilepath; //saved files path   
  337. private string sfilename; //saved files name   
  338. private int nsplitter; //count of splited downloading file   
  339.     
  340. public siteinfobean()   
  341. {   
  342. //default value of nsplitter is 5   
  343. this("","","",5);   
  344. }   
  345.     
  346. public siteinfobean(string surl,string spath,string sname,int nspiltter)   
  347. {   
  348. ssiteurl= surl;   
  349. sfilepath = spath;   
  350. sfilename = sname;   
  351. this.nsplitter = nspiltter;     
  352. }   
  353.     
  354. public string getssiteurl()   
  355. {   
  356. return ssiteurl;   
  357. }   
  358.     
  359. public void setssiteurl(string value)   
  360. {   
  361. ssiteurl = value;   
  362. }   
  363.     
  364. public string getsfilepath()   
  365. {   
  366. return sfilepath;   
  367. }   
  368.     
  369. public void setsfilepath(string value)   
  370. {   
  371. sfilepath = value;   
  372. }   
  373.    
  374. public string getsfilename()   
  375. {   
  376. return sfilename;   
  377. }   
  378.    
  379. public void setsfilename(string value)   
  380. {   
  381. sfilename = value;   
  382. }   
  383.    
  384. public int getnsplitter()   
  385. {   
  386. return nsplitter;   
  387. }    
  388.   
  389. public void setnsplitter(int ncount)   
  390. {   
  391. nsplitter = ncount;   
  392. }   
  393. }     
  394.   
  395. /*  
  396. **utility.java  
  397. */  
  398. package netfox;   
  399.    
  400. public class utility {   
  401.    
  402. public utility()   
  403. {    
  404. }   
  405.     
  406. public static void sleep(int nsecond)   
  407. {   
  408. try{   
  409. thread.sleep(nsecond);   
  410. }   
  411. catch(exception e)   
  412. {   
  413. e.printstacktrace ();   
  414. }   
  415. }   
  416.    
  417. public static void log(string smsg)   
  418. {   
  419. system.err.println(smsg);   
  420. }   
  421.    
  422. public static void log(int smsg)   
  423. {   
  424. system.err.println(smsg);   
  425. }   
  426. }   
  427.    
  428. /*  
  429. **testmethod.java  
  430. */  
  431. package netfox;   
  432.     
  433. public class testmethod {   
  434.    
  435. public testmethod()   
  436. ///xx/weblogic60b2_win.exe   
  437. try{   
  438. siteinfobean bean = new siteinfobean("http://localhost/xx/weblogic60b2_win.exe","l:\\temp","weblogic60b2_win.exe",5);   
  439. //siteinfobean bean = new siteinfobean("http://localhost:8080/down.zip","l:\\temp","weblogic60b2_win.exe",5);   
  440. sitefilefetch filefetch = new sitefilefetch(bean);   
  441. filefetch.start();   
  442. }   
  443. catch(exception e){e.printstacktrace ();}     
  444. }   
  445.    
  446. public static void main(string[] args)   
  447. {   
  448. new testmethod();   
  449. }   
  450. }   
本文发布:Android开发网
本文地址:http://www.jizhuomi.com/android/example/342.html
2013年1月3日
发布:鸡啄米 分类:Android开发实例 浏览: 评论:2