1、java 实现断点续传 java实现断点续传(一)断点续传的原理其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已。打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:假设服务器域名为,文件名为down.zip。GET /down.zip HTTP/1.1Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-Excel, application/msWord, application/vnd.ms-Powerpoint, */*Accept-Language:
2、zh-cnAccept-Encoding: gzip, deflateUser-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)Connection: Keep-Alive服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:200Content-Length=106786028Accept-Ranges=bytesDate=Mon, 30 Apr 2001 12:56:11 GMTETag=W/02ca57e173c11:95bContent-Type=application/octet-s
3、treamServer=Microsoft-IIS/5.0Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给Web服务器的时候要多加一条信息-从哪里开始。下面是用自己编的一个浏览器来传递请求信息给Web服务器,要求从2000070字节开始。GET /down.zip HTTP/1.0User-Agent: NetFoxRANGE: bytes=2000070-Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2仔细
4、看一下就会发现多了一行RANGE: bytes=2000070-这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。服务器收到这个请求以后,返回的信息如下:206Content-Length=106786028Content-Range=bytes 2000070-106786027/106786028Date=Mon, 30 Apr 2001 12:55:20 GMTETag=W/02ca57e173c11:95bContent-Type=application/octet-streamServer=Microsoft-IIS/5.0Last-M
5、odified=Mon, 30 Apr 2001 12:55:20 GMT和前面服务器返回的信息比较一下,就会发现增加了一行:Content-Range=bytes 2000070-106786027/106786028返回的代码也改为206了,而不再是200了。知道了以上原理,就可以进行断点续传的编程了。(二)Java实现断点续传的关键几点用什么方法实现提交RANGE: bytes=2000070-。当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Java的net包中提供了这种功能。代码如下:URL url = new URL(HttpURLConnection httpCo
6、nnection = (HttpURLConnection)url.openConnection();/设置User-AgenthttpConnection.setRequestProperty(User-Agent,NetFox);/设置断点续传的开始位置httpConnection.setRequestProperty(RANGE,bytes=2000070);/获得输入流InputStream input = httpConnection.getInputStream();从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。大家看,其实断点续传用Java实现起来还
7、是很简单的吧。接下来要做的事就是怎么保存获得的流到文件中去了。保存文件采用的方法。我采用的是IO包中的RandAccessFile类。操作相当简单,假设从2000070处开始保存文件,代码如下:RandomAccess oSavedFile = new RandomAccessFile(down.zip,rw);long nPos = 2000070;/定位文件指针到nPos位置oSavedFile.seek(nPos);byte b = new byte1024;int nRead;/从输入流中读入字节流,然后写到文件中while(nRead=input.read(b,0,1024) 0)
8、oSavedFile.write(b,0,nRead);怎么样,也很简单吧。接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。(三)断点续传内核的实现主要用了6个类,包括一个测试类。1: SiteFileFetch.Java - 负责整个文件的抓取,控制内部线程(FileSplitterFetch)。2: FileSplitterFetch.Java - 负责部分文件的抓取。3: FileAccess.Java - 负责文件的存储。4: SiteInfoBean.Java - 要抓取的文件的信息,如文件保存的目录,名字,抓取文件的URL等。5: Utility.Java - 工
9、具类,放一些简单的方法。6: TestMethod.Java - 测试类。首先创建继承Thread类的传输文件线程类,其JAVA文件名为SiteFileFetch.java,代码如下:import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import .HttpURLConnection;import .U
10、RL;/* * 传输文件线程类。 * author zhang */public class SiteFileFetch extends Thread SiteInfoBean siteInfoBean = null; /* 文件位置指针 */ long nPos; /* 开始位置 */ long nStartPos; /* 结束位置 */ long nEndPos; /* 子线程对象 */ FileSplitterFetch fileSplitterFetch; /* 文件长度 */ long nFileLength; /* 是否第一次读取 */ boolean bFirst = true;
11、 /* 停止标志 */ boolean bStop = false; /* 文件传输临时信息 */ File tmpFile; / /* 输出到文件的输出流 */ DataOutputStream output; public SiteFileFetch(SiteInfoBean bean) throws IOException siteInfoBean = bean; tmpFile = new File(bean.getSFilePath() + File.separator + bean.getSFileName() + .info); if (tmpFile.exists() bFir
12、st = false; read_nPos(); else nStartPos = new longbean.getNSplitter(); nEndPos = new longbean.getNSplitter(); public void run() try if (bFirst) / 获得文件长度 nFileLength = getFileSize(); if (nFileLength = -1) System.err.println(File Length is not known); else if (nFileLength = -2) System.err.println(File
13、 is not access!); else / 分割下载文件 for (int i = 0; i nStartPos.length; i+) nStartPosi = (long) (i * (nFileLength / nStartPos.length); for (int i = 0; i nEndPos.length - 1; i+) nEndPosi = nStartPosi + 1; nEndPosnEndPos.length - 1 = nFileLength; / 创建FileSplitterFetch类实例 fileSplitterFetch = new FileSplitt
14、erFetchnStartPos.length; / 启动FileSplitterFetch线程 for (int i = 0; i nStartPos.length; i+) fileSplitterFetchi = new FileSplitterFetch(siteInfoBean.getSSiteURL(),siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),nStartPosi, nEndPosi, i); Utility.log(Thread + i + ,nStartPos= + n
15、StartPosi + ,nEndPos= + nEndPosi); fileSplitterFetchi.start(); boolean breakWhile = false; / 等待子线程结束 while (!bStop) write_nPos(); Utility.sleep(500); breakWhile = true; for (int i = 0; i = 400) processErrorCode(responseCode); / -2为WEB服务器响应错误 return -2; String sHeader; for (int i = 1; i+) sHeader = h
16、ttpConnection.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 n
17、FileLength; /* * 保存传输文件指针位置 */ 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(fileSplitterFetchi.nStartPos); output.writeLong(fileSplitterFetchi.nEndPos); output.cl
18、ose(); 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 longnCount; nEndPos = new longnCount;
19、for (int i = 0; i nStartPos.length; i+) nStartPosi = input.readLong(); nEndPosi = 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 vo
20、id siteStop() bStop = true; for (int i = 0; i nStartPos.length; i+) fileSplitterFetchi.splitterStop(); 创建继承Thread类的将要传输的网络文件分割线程类,文件名为FileSplitterFetch.javaimport java.io.IOException;import java.io.InputStream;import .HttpURLConnection;import .URL;public class FileSplitterFetch extends Thread /* 定义文
21、件传输时使用的变量 */ String sURL; /* 分段文件传输开始位置 */ long nStartPos; /* 分段文件传输结束位置 */ long nEndPos; /* 子线程ID */ int nThreadID; /* 完成文件传输 */ boolean bDownOver = false; /* 停止文件传输 */ boolean bStop = false; FileAccess fileAccess = null; /* * * param sURL * param sName * param nStart * param nEnd * param id * thro
22、ws IOException */ 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; / 创建文件并打开 fileAccess = new FileAccess(sName, nStartPos); /* * */ public void run() while (nStartPos
23、 0 & nStartPos nEndPos & !bStop) nStartPos += fileAccess.write(b, 0, nRead); Utility.log(Thread + nThreadID + is over!); bDownOver = true; catch (Exception e) e.printStackTrace(); /* * 处理和响应服务器头数据。 * param con */ public void logResponseHead(HttpURLConnection con) for (int i = 1; i+) String header =
24、con.getHeaderFieldKey(i); if (header != null) Utility.log(header + : + con.getHeaderField(header); else break; public void splitterStop() bStop = true; 创建设置和获取网络信息类,类名为SiteInfoBean.java /* * 定义获取和设置相关文件类信息。 * author zhang */public class SiteInfoBean /* 定义URL变量 */ private String sSiteURL; /* 定义存文件路径变量 */ private String sFilePath; /* 定义文件名变量 */ private String sFileName; /* 定义传输文件计数器 */ private int nSplitter; public SiteInfoBean() this(, , , 5); public SiteInf
copyright@ 2008-2023 冰点文库 网站版权所有
经营许可证编号:鄂ICP备19020893号-2