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

用什么方法实现提交RANGE: bytes=2000070-
当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Javanet包中提供了这种功能。代码如下:
URL url = new URL("www.sjtu.edu/down.zip");
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
//设置User-Agent
httpConnection.setRequestProperty("User-Agent","NetFox");
//设置断点续传的开始位置
httpConnection.setRequestProperty("RANGE","bytes=2000070");
//获得输入流
InputStream input = InputStream();

从输入流中取出的字节流就是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((ad(b,0,1024)) > 0){
    oSavedFile.write(b,0,nRead);
}
怎么样,也很简单吧。
接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。

()断点续传内核的实现
主要用了6个类,包括一个测试类。
1 SiteFileFetch.Java    - 负责整个文件的抓取,控制内部线程(FileSplitterFetch)
2 FileSplitterFetch.Java - 负责部分文件的抓取。
3 FileAccess.Java        - 负责文件的存储。
4 SiteInfoBean.Java      - 要抓取的文件的信息,如文件保存的目录,名字,抓取文件的URL等。
5 Utility.Java          - 工具类,放一些简单的方法。
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 java.HttpURLConnection;
import java.URL;
/**
* 传输文件线程类。
* @author zhang
*/
public class SiteFileFetch extends Thread {
    SiteInfoBean siteInfoBean = null;
    /* 文件位置指针 */
    long[] nPos;
    /* 开始位置 */
    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 = new SFilePath() + File.separator + SFileName() + ".info");
        if (tmpFile.exists()) {
            bFirst = false;
            read_nPos();
        } else {
            nStartPos = new long[NSplitter()];
            nEndPos = new long[NSplitter()];
        }
    }
    public void run() {
        try {
            if (bFirst) {
                java创建文件// 获得文件长度
                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类实例
            fileSplitterFetch = new FileSplitterFetch[nStartPos.length];
            // 启动FileSplitterFetch线程
            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("Thread " + i + ",nStartPos=" + nStartPos[i] + " ,nEndPos= " + nEndPos[i]);
                fileSplitterFetch[i].start();
            }
            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;
                    }
                }
                // 是否结束while循环
                if (breakWhile)
                    break;
            }
            System.out.println("文件传输结束!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
    * 获得文件长度
    * @return
    */
    public long getFileSize() {
        int nFileLength = -1;
        try {
            // 创建与WEB服务器的连接
            URL url = new URL(siteInfoBean.getSSiteURL());
            HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
            httpConnection.setRequestProperty("User-Agent", "sumebrokentransfer");
            int responseCode = ResponseCode();
            if (responseCode >= 400) {
                processErrorCode(responseCode);
                // -2WEB服务器响应错误
                return -2;
            }
            String sHeader;
            for (int i = 1;; i++) {
                sHeader = HeaderFieldKey(i);
                if (sHeader != null) {
                    if (sHeader.equals("Content-Length")) {
                        nFileLength = Integer.parseInt(HeaderField(sHeader));
                        break;
                    }
                } else {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        Utility.log(nFileLength);
        return nFileLength;
    }
    /**
    * 保存传输文件指针位置
    */

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。