基於HTTP協議的下載功能實現


  超文本傳輸協議 (HTTP-HyperText Transfer Protocol)是一種使用極為廣泛的協議,它由請求和響應構成,是一種無狀態的應用層協議。設計HTTP協議的初衷是為了提供一種傳輸HTML(HyperText Markup Language,超文本標記語言)的協議和方法。通過HTTP協議請求的資源由URI(Uniform Resource Identifiers,統一資源標識符)來標識。通常,我們通過URI就能訪問到萬維網服務器提供的數據,如HTML文檔、音視頻等各種格式的數據。因此,基於HTTP協議,我們可以實現簡單的下載器功能。

  HTTP下載器的功能很簡單,下載器使用指定的URI發送GET請求到服務器,服務器返回請求響應,下載器根據響應狀態碼判斷請求成功與否。一般情況下,請求一個資源成功后的狀態碼應為200(HTTP_OK)或者206(Partial-Content)。其他狀態碼視為失敗(具體的HTTP狀態碼請參見:http://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81)。若請求成功,HTTP響應頭中將會帶有資源的相關信息,其中,我們最關心的是資源大小,一般以Content-Length字段返回(HTTP頭字段請參考:http://msdn.microsoft.com/zh-cn/library/aa287673(v=vs.71).aspx),若有Range字段的情況下,也可以通過Range字段的返回值來計算得到資源的大小,這里的資源大小單位為byte。

  下面就以Java語言來實現一個基於HTTP協議的下載器。因測試工程是為項目所編寫,實現了一套完整的下載管控機制(如下載記錄管理、多任務管理、斷點續下、意外中斷自動恢復下載、狀態通知等功能),為保密起見,以下只公開下載部分核心代碼:

  下載狀態類:

public class Status {
public static final int ERROR = -1; public static final int WAITING = 0; /** * Paused by system automatically */ public static final int PAUSED = 1; /** * Paused by user manually */ public static final int PAUSED_MANUALLY = 2; public static final int DOWNLOADING = 3; public static final int FINISHED = 4; }

  下載類:

package com.irwin.downloader;

import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import org.apache.http.HttpStatus;


public class Downloader implements Runnable {
    private static final int BUFFER_SIZE = 32 * 1024;

    private String mUrl = null;

    private String mPath = null;

    private long mStart = 0;

    private long mCurrentBytes = 0;

    private int mStatus = Status.WAITING;

    private long mSize = 0;

    private boolean mRunning = true;

    private boolean mTerminate = false;

    public void setUrl(String url) {
        mUrl = url;
    }

    public String getUrl() {
        return mUrl;
    }

    public void setPath(String path) {
        mPath = path;
    }

    public String getPath() {
        return mPath;
    }

    public void setRange(long start, long end) {
        if (start >= 0) {
            mStart = start;
        }
        mSize = end;
    }

    public void setTerminate(boolean terminate) {
        mTerminate = terminate;
    }

    public boolean isRunning() {
        return mRunning;
    }

    public int getStatus() {

        return mStatus;
    }

    public long getSize() {
        return mSize;
    }

    public long getCurrentBytes() {
        return mCurrentBytes;
    }

    public void download(String url, String path, long rangeStart)
            throws Exception {
        if (rangeStart > 0 && rangeStart >= mSize) {
            mStatus = Status.FINISHED;
            return;
        }
        InputStream inStream = null;
        RandomAccessFile file = null;
        try {
            URL downUrl = new URL(url);
            HttpURLConnection http = (HttpURLConnection) downUrl
                    .openConnection();
            http.setConnectTimeout(5 * 1000);
            //Use Get method
            http.setRequestMethod("GET");
            //Accept all format of data.
            http.setRequestProperty("Accept", "*/*");
            http.setRequestProperty("Charset", "UTF-8");
            
            //Data block to download. 
            http.setRequestProperty("Range", "bytes=" + rangeStart + "-");
            http.setRequestProperty("User-Agent", "Client/4.0");
            http.setRequestProperty("Connection", "Keep-Alive");
            http.connect();
            if (http.getResponseCode() != HttpStatus.SC_OK
                    && http.getResponseCode() != HttpStatus.SC_PARTIAL_CONTENT) {
                throw new IllegalAccessException("Invalid request: "
                        + http.getResponseCode());
            }
            if (mSize <= 0) {
                long total = getContentLen(http);

                if (total <= 0) {
                    throw new IllegalAccessException("Invalid content-length: "
                            + total);
                }
                mSize = total;
            }
            mStatus = Status.DOWNLOADING;
            inStream = http.getInputStream();
            byte[] buffer = new byte[BUFFER_SIZE];
File tmp=new File(path+".tmp"); file
= new RandomAccessFile(tmp, "rwd"); file.setLength(mSize); file.seek(rangeStart); int offset = 0; mCurrentBytes = rangeStart; while (true) { if (mTerminate) { mStatus = Status.PAUSED_MANUALLY; break; } if ((offset = inStream.read(buffer)) != -1) { file.write(buffer, 0, offset); mCurrentBytes += offset; } else { break; } } file.close(); file = null; inStream.close(); inStream = null; if (mSize == mCurrentBytes) { // Rename mStatus = Status.FINISHED; File f = new File(path); tmp.renameTo(f); } } catch (Exception e) { throw e; } finally { if (inStream != null) { try { inStream.close(); } catch (Exception e2) { // TODO: handle exception } } if (file != null) { try { file.close(); } catch (Exception e2) { // TODO: handle exception } } } } @Override public void run() { if (getUrl() != null && getPath() != null) { if (!mTerminate) { try { download(getUrl(), getPath(), mStart); } catch (Exception e) { e.printStackTrace(); mStatus = Status.ERROR; } } } else { mStatus = Status.ERROR; } mRunning = false; } private long getContentLen(HttpURLConnection conn) throws Exception { long len = conn.getContentLength(); if (len <= 0) { try { len = Long.parseLong(conn.getHeaderField("Content-Length")); } catch (Exception e) { // TODO: handle exception } } if (len <= 0) { //Try to calculate size from 'Range' field. String range = conn.getHeaderField("Range"); if (range != null) { String[] array = range.replace("bytes=", "").split("-"); if (array.length == 2) { len = Long.parseLong(array[1]) - Long.parseLong(array[0]); } } } return len; } }

 更多代碼詳情請參考:TransferLibrary——一個Android文件傳輸庫,主要實現基於Http的文件上傳和下載,簡單方便,支持多任務下載,斷點續傳等等,歡迎小伙伴們使用交流:D


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM