Java實現文件下載斷點續傳(一)

參考文章:https://www.ibm.com/developerworks/cn/java/joy-down/

1.原理介紹

想象一下我們下載一個10G的文件,當下載到9.99G的時候斷網了。。。

斷點續傳支持從文件上次中斷的地方開始傳送數據,而並非是從文件開頭傳送。

體現在HTTP請求和響應上就會有區別

假設下載文件爲:

http://dl_dir.qq.com/invc/cyclone/QQDownload_Setup_39_708_p1.exe

普通文件下載的請求和響應

斷點續傳時候的請求和響應

這地方需要攔截HttpURLConnection的請求。

 

2.如何用Java實現

主要利用的是RandomAccessFile類的seek方法。

RandomAccessFile的特點在於任意訪問文件的任意位置,是基於字節訪問的;
可通過getFilePointer()獲取當前指針所在位置 ;
可通過seek()移動指針,這體現了它的任意性;
seek用於設置文件指針位置,設置後會從當前指針的下一位讀取或寫入。

這地方我在IBM文章基礎上簡化並調整了一版,自測了下。搬上來分享一下。


 

 

2.1 工具類

package com.laoxu.demo.breaktransfer;

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);
    }
}

2.2 文件信息實體

package com.laoxu.demo.breaktransfer;

/**
 *  遠程下載文件信息
 */
public class SiteFileInfo {
    private String sSiteURL; //文件下載鏈接
    private String sFilePath; //保存的文件路徑
    private String sFileName; //保存的文件名稱
    public SiteFileInfo()
    {
        this("","","");
    }
    public SiteFileInfo(String sURL, String sPath, String sName)
    {
        sSiteURL= sURL;
        sFilePath = sPath;
        sFileName = sName;
    }
    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;
    }
}

2.3 寫文件類

package com.laoxu.demo.breaktransfer;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;

/**
 *  寫文件
 */
public class FileAccess implements Serializable{
    RandomAccessFile oSavedFile;
    long nPos;
    public FileAccess() throws IOException
    {
        this("",0);
    }
    public FileAccess(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;
    }
}

2.4 主程序

package com.laoxu.demo.breaktransfer;

import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;

/**
 * @Description: 斷點續傳主程序
 * @Author laoxu
 * @Date 2019/12/7 15:31
 **/
public class SiteFileFetch{
    SiteFileInfo fileInfo = null; // 文件信息實體
    long startPos;    // 開始位置
    long endPos;      // 結束位置
    long fileLength; // 文件長度

    File localFile = null;          // 保存的文件
    String localFilePath;      // 本地文件路徑
    boolean bFirst = true; // 是否第一次取文件
    boolean bStop = false; // 停止標誌


    public SiteFileFetch(SiteFileInfo entity){
        fileInfo = entity;
        localFilePath = entity.getSFilePath()+File.separator+entity.getSFileName();
        localFile = new File(localFilePath);
        // 判斷本地文件是否存在
        if(localFile.exists()){
            startPos = localFile.length();
            bFirst = false;
        }
        fileLength = getFileSize();

        if(fileLength < 0){
            System.err.println("非法文件!");
        }else{
            endPos = fileLength;
        }

    }

    public void download() {
        if(startPos<endPos){
            try {
                FileAccess fileAccess = new FileAccess(localFilePath,startPos);

                // 配置fiddler代理
                Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888));
                URL url = new URL(fileInfo.getSSiteURL());
                HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection(proxy);

                httpConnection.setRequestProperty("User-Agent", "LX");
                String sProperty = "bytes=" + startPos + "-";
                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 && startPos < endPos
                        && !bStop) {
                    startPos += fileAccess.write(b, 0, nRead);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else {
            System.out.println("文件已存在!");
        }

    }

    // 獲得文件長度
    private long getFileSize() {
        int nFileLength = -1;
        try {
            URL url = new URL(fileInfo.getSSiteURL());
            HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
            httpConnection.setRequestProperty("User-Agent", "Lx");
            int responseCode = httpConnection.getResponseCode();
            if (responseCode >= 400) {
                System.out.println(responseCode);
                return -2; //-2 represent access is error
            }
            String sHeader;
            for (int i = 1; ; i++) {
                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;
    }
}

2.5 測試程序

package com.laoxu.demo.breaktransfer;

public class TestMethod {
    public TestMethod() {
        try {
            SiteFileInfo fileInfo = new SiteFileInfo("http://dl_dir.qq.com/invc/cyclone/QQDownload_Setup_39_708_p1.exe",
                    "D:\\tmp",
                    "QQDownload_Setup_39_708_p1.exe");
            SiteFileInfo fileInfo2 = new SiteFileInfo("http://www.luohanye.com/astros.json",
                    "D:\\tmp",
                    "astros.json");
            SiteFileFetch fileFetch = new SiteFileFetch(fileInfo);
            fileFetch.download();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new TestMethod();
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章