选择显示字体大小

用java实现断点续传(http)

java实现断点续传(http)


(一)断点续传的原理
(二)java实现断点续传的关键几点
(三)断点续传内核的实现


关于作者

钟华 (zhong_hua@263.net)
2001 年 5 月

(一)断点续传的原理
其实断点续传的原理很简单,就是在http的请求上和一般的下载有所不同而已。
打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:
假设服务器域名为wwww.sjtu.edu.cn,文件名为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实现断点续传的关键几点


(1)用什么方法实现提交range: bytes=2000070-。
当然用最原始的socket是肯定能完成的,不过那样太费事了,其实java.net包中提供了这种功能。代码如下:
url url = new url(" http://www.sjtu.edu.cn/down.zip";);
httpurlconnection httpconnection = (httpurlconnection)url.openconnection


();
//设置user-agent
httpconnection.setrequestproperty("user-agent",&quot.netfox");
//设置断点续传的开始位置
httpconnection.setrequestproperty("range","bytes=2000070");
//获得输入流
inputstream input = httpconnection.getinputstream();


从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。
大家看,其实断点续传用java实现起来还是很简单的吧。
接下来要做的事就是怎么保存获得的流到文件中去了。


保存文件采用的方法。
我采用的是io包中的randaccessfile类。
操作相当简单,假设从2000070处开始保存文件,代码如下:
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测试类。


下面是源程序:
/*
**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[i] = (long)(i*(nfilelength/nstartpos.length));
}
for(int i=0;i<nendpos.length-1;i++)
{
nendpos[i] = nstartpos[i+1];
}
nendpos[nendpos.length-1] = nfilelength;
}
}


//启动子线程
filesplitterfetch = new filesplitterfetch[nstartpos.length];
for(int i=0;i<nstartpos.length;i++)
{
filesplitterfetch[i] = new filesplitterfetch(siteinfobean.getssiteurl(),
siteinfobean.getsfilepath() + file.separator + siteinfobean.getsfilename(),
nstartpos[i],nendpos[i],i);
utility.log(&quot;thread &quot; + i + &quot; , nstartpos = &quot; + nstartpos[i] + &quot;, nendpos = &quot; + nendpos[i]);
filesplitterfetch[i].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(&quot;thread &quot; + (npos.length-1) + &quot; , nstartpos = &quot; + npos[npos.length-1] + &quot;,
nendpos = &quot; + 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[i].bdownover)
{
breakwhile = false;
break;
}
}
if(breakwhile)
break;


//count++;
//if(count>4)
// sitestop();
}


system.err.println(&quot;文件下载结束!&quot;);
}
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(&quot;user-agent&quot;,&quot.netfox&quot;);


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(&quot;content-length&quot;))
{
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[i]);
output.writelong(filesplitterfetch[i].nstartpos);
output.writelong(filesplitterfetch[i].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[i] = input.readlong();
nendpos[i] = input.readlong();
}
input.close();
}
catch(ioexception e){e.printstacktrace ();}
catch(exception e){e.printstacktrace ();}
}


private void processerrorcode(int nerrorcode)
{
system.err.println(&quot;error code : &quot; + nerrorcode);
}


//停止文件下载
public void sitestop()
{
bstop = true;
for(int i=0;i<nstartpos.length;i++)
filesplitterfetch[i].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; //thread's 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(&quot;user-agent&quot;,&quot.netfox&quot;);
string sproperty = &quot;bytes=&quot;+nstartpos+&quot;-&quot;;
httpconnection.setrequestproperty(&quot;range&quot;,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(&quot;nstartpos = &quot; + nstartpos + &quot;, nendpos = &quot; + nendpos);
}


utility.log(&quot;thread &quot; + nthreadid + &quot; is over!&quot;);
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+&quot; : &quot;+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(&quot;&quot;,0);
}


public fileaccessi(string sname,long npos) throws ioexception
{
osavedfile = new randomaccessfile(sname,&quot;rw&quot;);
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; //site's url
private string sfilepath; //saved file's path
private string sfilename; //saved file's name
private int nsplitter; //count of splited downloading file


public siteinfobean()
{
//default value of nsplitter is 5
this(&quot;&quot;,&quot;&quot;,&quot;&quot;,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(&quot; http://localhost/xx/weblogic60b2_win.exe&quot;;,&quot;l:\temp&quot;,&quot;weblogic60b2_win.exe&quot;,5);
//siteinfobean bean = new siteinfobean(&quot; http://localhost:8080/down.zip&quot;;,&quot;l:\temp&quot;,&quot;weblogic60b2_win.exe&quot;,5);
sitefilefetch filefetch = new sitefilefetch(bean);
filefetch.start();
}
catch(exception e){e.printstacktrace ();}


}


public static void main(string[] args)
{
new testmethod();
}
}


 


关键字 本文所属关键字

相关 与本文相关文章

分类 所有文章关键字导航

源码编程相关

Java   Asp   PHP   .Net   XML   C/C++   CGI   VB   Jsp   J2ee   J2se   J2me   EJB   Servlet   Tomcat   Resin   Struts   Weblogic   Eclipse   ANT   GUI   JMS   Web servise   IDEA   Webphere   Hibernate   Spring   Jboss   Applet   Swing   Socket   Javamail   Perl   Ajax   P2P   安全   模式   框架   测试   开源   游戏

SQL数据库相关

My-SQL   Ms-SQL   Access   DB2   Oracle   Sybase   SQLserver   索引   存储过程   加密   数据库   分页   视图  

手机无线相关

3G   Wap   CDMA   GRPS   GSM   IVR   彩信   短信   无线   增值业务

网页设计制作相关

HTML   CSS   网页配色   网页特效   Javascript   VBscript   Dreamweaver   Frontpage   JS   Web   网站设计

网站建设推广相关

建站经验   网站优化   网站排名   推广   Alexa

操作系统/服务器相关

Windows XP   Windows 2000   Windows 2003   Windows Me   Windows 9.x   Linux   UNIX   注册表   操作系统   服务器   应用服务器

图形图像多媒体相关

Photoshop   Fireworks   Flash   Coreldraw   Illustrator   Freehand   Photoimpact   多媒体   图形图像

标准 网站致力的规范

Valid CSS!

无不良内容,无不良广告,无恶意代码

Valid XHTML 1.0 Transitional

creativecommons