Android 斷點續(xù)傳的原理剖析與實例講解
本文所要講的是Android斷點續(xù)傳的內(nèi)容,以實例的形式進行了詳細(xì)介紹。
一、斷點續(xù)傳的原理
其實斷點續(xù)傳的原理很簡單,就是在http的請求上和一般的下載有所不同而已。
打個比方,瀏覽器請求服務(wù)器上的一個文時,所發(fā)出的請求如下:
假設(shè)服務(wù)器域名為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
服務(wù)器收到請求后,按要求尋找請求的文件,提取文件的信息,然后返回給瀏覽器,返回信息如下:
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
所謂斷點續(xù)傳,也就是要從文件已經(jīng)下載的地方開始繼續(xù)下載。所以在客戶端瀏覽器傳給web服務(wù)器的時候要多加一條信息--從哪里開始。
下面是用自己編的一個“瀏覽器”來傳遞請求信息給web服務(wù)器,要求從2000070字節(jié)開始。
get /down.zip http/1.0
user-agent: netfox
range: bytes=2000070-
accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
仔細(xì)看一下就會發(fā)現(xiàn)多了一行 range: bytes=2000070-
這一行的意思就是告訴服務(wù)器down.zip這個文件從2000070字節(jié)開始傳,前面的字節(jié)不用傳了。
服務(wù)器收到這個請求以后,返回的信息如下:
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
和前面服務(wù)器返回的信息比較一下,就會發(fā)現(xiàn)增加了一行:
content-range=bytes 2000070-106786027/106786028
返回的代碼也改為206了,而不再是200了。
知道了以上原理,就可以進行斷點續(xù)傳的編程了。
二、java實現(xiàn)斷點續(xù)傳的關(guān)鍵幾點
用什么方法實現(xiàn)提交range: bytes=2000070-?
當(dāng)然用最原始的socket是肯定能完成的,不過那樣太費事了,其實java的net包中提供了這種功能。代碼如下:
Java代碼
url url = new url("http://www.jizhuomi.com/android/down.zip"); httpurlconnection httpconnection = (httpurlconnection)url.openconnection(); //設(shè)置user-agent httpconnection.setrequestproperty("user-agent","netfox"); //設(shè)置斷點續(xù)傳的開始位置 httpconnection.setrequestproperty("range","bytes=2000070"); //獲得輸入流 inputstream input = httpconnection.getinputstream(); 從輸入流中取出的字節(jié)流就是down.zip文件從2000070開始的字節(jié)流。 大家看,其實斷點續(xù)傳用java實現(xiàn)起來還是很簡單的吧。 接下來要做的事就是怎么保存獲得的流到文件中去了。 保存文件采用的方法:我采用的是io包中的randaccessfile類。 操作相當(dāng)簡單,假設(shè)從2000070處開始保存文件,代碼如下: Java代碼 randomaccess osavedfile = new randomaccessfile("down.zip","rw"); long npos = 2000070; //定位文件指針到npos位置 osavedfile.seek(npos); byte[] b = new byte[1024]; int nread; //從輸入流中讀入字節(jié)流,然后寫到文件中 while((nread=input.read(b,0,1024)) > 0) { osavedfile.write(b,0,nread); } 怎么樣,也很簡單吧。 接下來要做的就是整合成一個完整的程序了。包括一系列的線程控制等等。 三、斷點續(xù)傳內(nèi)核的實現(xiàn) 主要用了6個類,包括一個測試類。 sitefilefetch.java負(fù)責(zé)整個文件的抓取,控制內(nèi)部線程(filesplitterfetch類)。 filesplitterfetch.java負(fù)責(zé)部分文件的抓取。 fileaccess.java負(fù)責(zé)文件的存儲。 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; //結(jié)束位置 filesplitterfetch[] filesplitterfetch; //子線程對象 long nfilelength; //文件長度 boolean bfirst = true; //是否第一次取文件 boolean bstop = false; //停止標(biāo)志 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(); //等待子線程結(jié)束 //int count = 0; //是否結(jié)束while循環(huán) 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("文件下載結(jié)束!"); } 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 ();} } } //打印回應(yīng)的頭信息 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 斷點續(xù)傳的資料整理,后續(xù)繼續(xù)補充相關(guān)資料,謝謝大家對本站的支持!
- 詳解Android使用OKHttp3實現(xiàn)下載(斷點續(xù)傳、顯示進度)
- android實現(xiàn)多線程下載文件(支持暫停、取消、斷點續(xù)傳)
- android使用OkHttp實現(xiàn)下載的進度監(jiān)聽和斷點續(xù)傳
- Android FTP 多線程斷點續(xù)傳下載\上傳的實例
- Android多線程斷點續(xù)傳下載功能實現(xiàn)代碼
- Android多線程+單線程+斷點續(xù)傳+進度條顯示下載功能
- Android實現(xiàn)網(wǎng)絡(luò)多線程斷點續(xù)傳下載實例
- Android 斷點續(xù)傳原理以及實現(xiàn)
- Android編程開發(fā)實現(xiàn)多線程斷點續(xù)傳下載器實例
- Android快速實現(xiàn)斷點續(xù)傳的方法
相關(guān)文章
Android監(jiān)聽鍵盤狀態(tài)獲取鍵盤高度的實現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android監(jiān)聽鍵盤狀態(tài)獲取鍵盤高度的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Android自定義View實現(xiàn)水波紋引導(dǎo)動畫
這篇文章主要為大家詳細(xì)介紹了Android自定義View實現(xiàn)水波紋動畫引導(dǎo),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01Android?studio?利用共享存儲進行用戶的注冊和登錄驗證功能
這篇文章主要介紹了Android?studio?利用共享存儲進行用戶的注冊和登錄驗證功能,包括注冊頁面布局及登錄頁面功能,本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-12-12Android開發(fā)之Android studio的安裝與使用
本文是此系列文章的第一篇,主要給大家講述的是Android studio的安裝與使用,十分的詳細(xì),有需要的小伙伴可以參考下2016-02-02Androd 勇闖高階性能優(yōu)化之布局優(yōu)化篇
Android性能優(yōu)化方面也有很多文章了,這里就做一個總結(jié),從原理到方法,工具等做一個簡單的了解,從而可以慢慢地改變編碼風(fēng)格,從而提高性能2021-10-10如何自己實現(xiàn)Android View Touch事件分發(fā)流程
這篇文章主要介紹了如何自己實現(xiàn)Android View Touch事件分發(fā)流程,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03