Android 斷點(diǎn)續(xù)傳的原理剖析與實(shí)例講解
本文所要講的是Android斷點(diǎn)續(xù)傳的內(nèi)容,以實(shí)例的形式進(jìn)行了詳細(xì)介紹。
一、斷點(diǎn)續(xù)傳的原理
其實(shí)斷點(diǎn)續(xù)傳的原理很簡單,就是在http的請求上和一般的下載有所不同而已。
打個(gè)比方,瀏覽器請求服務(wù)器上的一個(gè)文時(shí),所發(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
所謂斷點(diǎn)續(xù)傳,也就是要從文件已經(jīng)下載的地方開始繼續(xù)下載。所以在客戶端瀏覽器傳給web服務(wù)器的時(shí)候要多加一條信息--從哪里開始。
下面是用自己編的一個(gè)“瀏覽器”來傳遞請求信息給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這個(gè)文件從2000070字節(jié)開始傳,前面的字節(jié)不用傳了。
服務(wù)器收到這個(gè)請求以后,返回的信息如下:
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了。
知道了以上原理,就可以進(jìn)行斷點(diǎn)續(xù)傳的編程了。
二、java實(shí)現(xiàn)斷點(diǎn)續(xù)傳的關(guān)鍵幾點(diǎn)
用什么方法實(shí)現(xiàn)提交range: bytes=2000070-?
當(dāng)然用最原始的socket是肯定能完成的,不過那樣太費(fèi)事了,其實(shí)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è)置斷點(diǎn)續(xù)傳的開始位置
httpconnection.setrequestproperty("range","bytes=2000070");
//獲得輸入流
inputstream input = httpconnection.getinputstream();
從輸入流中取出的字節(jié)流就是down.zip文件從2000070開始的字節(jié)流。
大家看,其實(shí)斷點(diǎn)續(xù)傳用java實(shí)現(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);
}
怎么樣,也很簡單吧。
接下來要做的就是整合成一個(gè)完整的程序了。包括一系列的線程控制等等。
三、斷點(diǎn)續(xù)傳內(nèi)核的實(shí)現(xiàn)
主要用了6個(gè)類,包括一個(gè)測試類。
sitefilefetch.java負(fù)責(zé)整個(gè)文件的抓取,控制內(nèi)部線程(filesplitterfetch類)。
filesplitterfetch.java負(fù)責(zé)部分文件的抓取。
fileaccess.java負(fù)責(zé)文件的存儲。
siteinfobean.java要抓取的文件的信息,如文件保存的目錄,名字,抓取文件的url等。
utility.java工具類,放一些簡單的方法。
testmethod.java測試類。
四、實(shí)例源碼
下面是源程序:
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; //文件下載的臨時(shí)信息
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()
{
//獲得文件長度
//分割文件
//實(shí)例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 斷點(diǎn)續(xù)傳的資料整理,后續(xù)繼續(xù)補(bǔ)充相關(guān)資料,謝謝大家對本站的支持!
- 詳解Android使用OKHttp3實(shí)現(xiàn)下載(斷點(diǎn)續(xù)傳、顯示進(jìn)度)
- android實(shí)現(xiàn)多線程下載文件(支持暫停、取消、斷點(diǎn)續(xù)傳)
- android使用OkHttp實(shí)現(xiàn)下載的進(jìn)度監(jiān)聽和斷點(diǎn)續(xù)傳
- Android FTP 多線程斷點(diǎn)續(xù)傳下載\上傳的實(shí)例
- Android多線程斷點(diǎn)續(xù)傳下載功能實(shí)現(xiàn)代碼
- Android多線程+單線程+斷點(diǎn)續(xù)傳+進(jìn)度條顯示下載功能
- Android實(shí)現(xiàn)網(wǎng)絡(luò)多線程斷點(diǎn)續(xù)傳下載實(shí)例
- Android 斷點(diǎn)續(xù)傳原理以及實(shí)現(xiàn)
- Android編程開發(fā)實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳下載器實(shí)例
- Android快速實(shí)現(xiàn)斷點(diǎn)續(xù)傳的方法
相關(guān)文章
Android監(jiān)聽鍵盤狀態(tài)獲取鍵盤高度的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android監(jiān)聽鍵盤狀態(tài)獲取鍵盤高度的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
Android自定義View實(shí)現(xiàn)水波紋引導(dǎo)動畫
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)水波紋動畫引導(dǎo),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
Android?studio?利用共享存儲進(jìn)行用戶的注冊和登錄驗(yàn)證功能
這篇文章主要介紹了Android?studio?利用共享存儲進(jìn)行用戶的注冊和登錄驗(yàn)證功能,包括注冊頁面布局及登錄頁面功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-12-12
Android開發(fā)之Android studio的安裝與使用
本文是此系列文章的第一篇,主要給大家講述的是Android studio的安裝與使用,十分的詳細(xì),有需要的小伙伴可以參考下2016-02-02
Androd 勇闖高階性能優(yōu)化之布局優(yōu)化篇
Android性能優(yōu)化方面也有很多文章了,這里就做一個(gè)總結(jié),從原理到方法,工具等做一個(gè)簡單的了解,從而可以慢慢地改變編碼風(fēng)格,從而提高性能2021-10-10
Android程序設(shè)計(jì)之AIDL實(shí)例詳解
這篇文章主要介紹了Android程序設(shè)計(jì)的AIDL,以一個(gè)完整實(shí)例的形式較為詳細(xì)的講述了AIDL的原理及實(shí)現(xiàn)方法,需要的朋友可以參考下2014-09-09
Android sqlite cursor的遍歷實(shí)例詳解
在本篇內(nèi)容里小編給大家整理的是一篇關(guān)于Android sqlite cursor的遍歷的相關(guān)實(shí)例及知識點(diǎn),需要的朋友們可以學(xué)習(xí)下。2021-06-06
如何自己實(shí)現(xiàn)Android View Touch事件分發(fā)流程
這篇文章主要介紹了如何自己實(shí)現(xiàn)Android View Touch事件分發(fā)流程,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03
Android自定義流式布局的實(shí)現(xiàn)示例
這篇文章主要介紹了Android自定義流式布局的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12

