断点续传的原理
其实断点续传的原理很简单就是在 Http 的请求上和一般的下载有所不同而已
打个比方浏览器请求服务器上的一个文时所发出的请求如下
假设服务器域名为 文件名为 downzip
GET /downzip HTTP/
Accept: image/gif image/xxbitmap image/jpeg image/pjpeg application/vndms
excel application/msword application/vndmspowerpoint */*
AcceptLanguage: zhcn
AcceptEncoding: gzip deflate
UserAgent: Mozilla/ (compatible; MSIE ; Windows NT )
Connection: KeepAlive
服务器收到请求后按要求寻找请求的文件提取文件的信息然后返回给浏览器返回信息如下
ContentLength=
AcceptRanges=bytes
Date=Mon Apr :: GMT
ETag=W/caec:b
ContentType=application/octetstream
Server=MicrosoftIIS/
LastModified=Mon Apr :: GMT
所谓断点续传也就是要从文件已经下载的地方开始继续下载所以在客户端浏览器传给 Web 服务器的时候要多加一条信息 从哪里开始
下面是用自己编的一个浏览器来传递请求信息给 Web 服务器要求从 字节开始
GET /downzip HTTP/
UserAgent: NetFox
RANGE: bytes=
Accept: text/html image/gif image/jpeg *; q= */*; q=
仔细看一下就会发现多了一行 RANGE: bytes=
这一行的意思就是告诉服务器 downzip 这个文件从 字节开始传前面的字节不用传了
服务器收到这个请求以后返回的信息如下
ContentLength=
ContentRange=bytes /
Date=Mon Apr :: GMT
ETag=W/caec:b
ContentType=application/octetstream
Server=MicrosoftIIS/
LastModified=Mon Apr :: GMT
和前面服务器返回的信息比较一下就会发现增加了一行
ContentRange=bytes /
返回的代码也改为 了而不再是 了
知道了以上原理就可以进行断点续传的编程了
Java 实现断点续传的关键几点
() 用什么方法实现提交 RANGE: bytes=
当然用最原始的 Socket 是肯定能完成的不过那样太费事了其实 Java 的 net 包中提供了这种功能代码如下
URL url = new URL();
HttpURLConnection httpConnection = (HttpURLConnection)urlopenConnection();
// 设置 UserAgent
(UserAgentNetFox);
// 设置断点续传的开始位置
(RANGEbytes=);
// 获得输入流
InputStream input = ();
从输入流中取出的字节流就是 downzip 文件从 开始的字节流 大家看其实断点续传用 Java 实现起来还是很简单的吧 接下来要做的事就是怎么保存获得的流到文件中去了
保存文件采用的方法
我采用的是 IO 包中的 RandAccessFile 类
操作相当简单假设从 处开始保存文件代码如下
RandomAccess oSavedFile = new RandomAccessFile(downziprw);
long nPos = ;
// 定位文件指针到 nPos 位置
oSavedFileseek(nPos);
byte[] b = new byte[];
int nRead;
// 从输入流中读入字节流然后写到文件中
while((nRead=inputread(b)) > )
{
oSavedFilewrite(bnRead);
}
怎么样也很简单吧 接下来要做的就是整合成一个完整的程序了包括一系列的线程控制等等
断点续传内核的实现
主要用了 个类包括一个测试类
SiteFileFetchjava 负责整个文件的抓取控制内部线程 (FileSplitterFetch 类 )
FileSplitterFetchjava 负责部分文件的抓取
FileAccessjava 负责文件的存储
SiteInfoBeanjava 要抓取的文件的信息如文件保存的目录名字抓取文件的 URL 等
Utilityjava 工具类放一些简单的方法
TestMethodjava 测试类
下面是源程序
/*
/*
* SiteFileFetchjava
*/
package NetFox;
import javaio*;
import *;
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 = FilecreateTempFile (zhongnew File(beangetSFilePath()));
tmpFile = new File(beangetSFilePath()+Fileseparator + beangetSFileName()+);
if(tmpFileexists ())
{
bFirst = false;
read_nPos();
}
else
{
nStartPos = new long[beangetNSplitter()];
nEndPos = new long[beangetNSplitter()];
}
}
public void run()
{
// 获得文件长度
// 分割文件
// 实例 FileSplitterFetch
// 启动 FileSplitterFetch 线程
// 等待子线程返回
try{
if(bFirst)
{
nFileLength = getFileSize();
if(nFileLength == )
{
Systemerrprintln(File Length is not known!);
}
else if(nFileLength == )
{
Systemerrprintln(File is not access!);
}
else
{
for(int i=;i<nStartPoslength;i++)
{
nStartPos[i] = (long)(i*(nFileLength/nStartPoslength));
}
for(int i=;i<nEndPoslength;i++)
{
nEndPos[i] = nStartPos[i+];
}
nEndPos[nEndPoslength] = nFileLength;
}
}
// 启动子线程
fileSplitterFetch = new FileSplitterFetch[nStartPoslength];
for(int i=;i<nStartPoslength;i++)
{
fileSplitterFetch[i] = new FileSplitterFetch(siteInfoBeangetSSiteURL()
siteInfoBeangetSFilePath() + Fileseparator + siteInfoBeangetSFileName()
nStartPos[i]nEndPos[i]i);
Utilitylog(Thread + i + nStartPos = + nStartPos[i] + nEndPos =
+ nEndPos[i]);
fileSplitterFetch[i]start();
}
// fileSplitterFetch[nPoslength] = new FileSplitterFetch(siteInfoBeangetSSiteURL()
siteInfoBeangetSFilePath() + Fileseparator
+ siteInfoBeangetSFileName()nPos[nPoslength]nFileLengthnPoslength);
// Utilitylog(Thread +(nPoslength) + nStartPos = +nPos[nPoslength]+
nEndPos = + nFileLength);
// fileSplitterFetch[nPoslength]start();
// 等待子线程结束
//int count = ;
// 是否结束 while 循环
boolean breakWhile = false;
while(!bStop)
{
write_nPos();
Utilitysleep();
breakWhile = true;
for(int i=;i<nStartPoslength;i++)
{
if(!fileSplitterFetch[i]bDownOver)
{
breakWhile = false;
break;
}
}
if(breakWhile)
break;
//count++;
//if(count>)
// siteStop();
}
Systemerrprintln(文件下载结束!);
}
catch(Exception e){eprintStackTrace ();}
}
// 获得文件长度
public long getFileSize()
{
int nFileLength = ;
try{
URL url = new URL(siteInfoBeangetSSiteURL());
HttpURLConnection httpConnection = (HttpURLConnection)urlopenConnection ();
(UserAgentNetFox);
int responseCode=();
if(responseCode>=)
{
processErrorCode(responseCode);
return ; // represent access is error
}
String sHeader;
for(int i=;;i++)
{
//DataInputStream in = new DataInputStream( ());
//Utilitylog(inreadLine());
sHeader=(i);
if(sHeader!=null)
{
if(sHeaderequals(ContentLength))
{
nFileLength = IntegerparseInt((sHeader));
break;
}
}
else
break;
}
}
catch(IOException e){eprintStackTrace ();}
catch(Exception e){eprintStackTrace ();}
Utilitylog(nFileLength);
return nFileLength;
}
// 保存下载信息(文件指针位置)
private void write_nPos()
{
try{
output = new DataOutputStream(new FileOutputStream(tmpFile));
outputwriteInt(nStartPoslength);
for(int i=;i<nStartPoslength;i++)
{
// outputwriteLong(nPos[i]);
outputwriteLong(fileSplitterFetch[i]nStartPos);
outputwriteLong(fileSplitterFetch[i]nEndPos);
}
outputclose();
}
catch(IOException e){eprintStackTrace ();}
catch(Exception e){eprintStackTrace ();}
}
// 读取保存的下载信息(文件指针位置)
private void read_nPos()
{
try{
DataInputStream input = new DataInputStream(new FileInputStream(tmpFile));
int nCount = inputreadInt();
nStartPos = new long[nCount];
nEndPos = new long[nCount];
for(int i=;i<nStartPoslength;i++)
{
nStartPos[i] = inputreadLong();
nEndPos[i] = inputreadLong();
}
inputclose();
}
catch(IOException e){eprintStackTrace ();}
catch(Exception e){eprintStackTrace ();}
}
private void processErrorCode(int nErrorCode)
{
Systemerrprintln(Error Code : + nErrorCode);
}
// 停止文件下载
public void siteStop()
{
bStop = true;
for(int i=;i<nStartPoslength;i++)
fileSplitterFetch[i]splitterStop();
}
}
/*
**FileSplitterFetchjava
*/
package NetFox;
import javaio*;
import *;
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 sURLString sNamelong nStartlong nEndint id)
throws IOException
{
thissURL = sURL;
thisnStartPos = nStart;
thisnEndPos = nEnd;
nThreadID = id;
fileAccessI = new FileAccessI(sNamenStartPos);
}
public void run()
{
while(nStartPos < nEndPos && !bStop)
{
try{
URL url = new URL(sURL);
HttpURLConnection httpConnection = (HttpURLConnection)urlopenConnection ();
(UserAgentNetFox);
String sProperty = bytes=+nStartPos+;
(RANGEsProperty);
Utilitylog(sProperty);
InputStream input = ();
//logResponseHead(httpConnection);
byte[] b = new byte[];
int nRead;
while((nRead=inputread(b)) > && nStartPos < nEndPos
&& !bStop)
{
nStartPos += fileAccessIwrite(bnRead);
//if(nThreadID == )
// Utilitylog(nStartPos = + nStartPos + nEndPos = + nEndPos);
}
Utilitylog(Thread + nThreadID + is over!);
bDownOver = true;
//nPos = fileAccessIwrite (bnRead);
}
catch(Exception e){eprintStackTrace ();}
}
}
// 打印回应的头信息
public void logResponseHead(HttpURLConnection con)
{
for(int i=;;i++)
{
String header=congetHeaderFieldKey(i);
if(header!=null)
//responseHeadersput(header(header));
Utilitylog(header+ : +congetHeaderField(header));
else
break;
}
}
public void splitterStop()
{
bStop = true;
}
}
/*
**FileAccessjava
*/
package NetFox;
import javaio*;
public class FileAccessI implements Serializable{
RandomAccessFile oSavedFile;
long nPos;
public FileAccessI() throws IOException
{
this();
}
public FileAccessI(String sNamelong nPos) throws IOException
{
oSavedFile = new RandomAccessFile(sNamerw);
thisnPos = nPos;
oSavedFileseek(nPos);
}
public synchronized int write(byte[] bint nStartint nLen)
{
int n = ;
try{
oSavedFilewrite(bnStartnLen);
n = nLen;
}
catch(IOException e)
{
eprintStackTrace ();
}
return n;
}
}
/*
**SiteInfoBeanjava
*/
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
this();
}
public SiteInfoBean(String sURLString sPathString sNameint nSpiltter)
{
sSiteURL= sURL;
sFilePath = sPath;
sFileName = sName;
thisnSplitter = 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;
}
}
/*
**Utilityjava
*/
package NetFox;
public class Utility {
public Utility()
{
}
public static void sleep(int nSecond)
{
try{
Threadsleep(nSecond);
}
catch(Exception e)
{
eprintStackTrace ();
}
}
public static void log(String sMsg)
{
Systemerrprintln(sMsg);
}
public static void log(int sMsg)
{
Systemerrprintln(sMsg);
}
}
/*
**TestMethodjava
*/
package NetFox;
public class TestMethod {
public TestMethod()
{ ///xx/weblogicb_winexe
try{
SiteInfoBean bean = new SiteInfoBean(
L:\\tempweblogicb_winexe);
//SiteInfoBean bean = new SiteInfoBean(L:\\temp
weblogicb_winexe);
SiteFileFetch fileFetch = new SiteFileFetch(bean);
fileFetchstart();
}
catch(Exception e){eprintStackTrace ();}
}
public static void main(String[] args)
{
new TestMethod();
}
}