斷點續傳

咱們常常使用下載工具,如bit精靈、迅雷、FlashGet,這些軟件都支持斷點續傳。html

斷點續傳即下載任務暫停後能夠繼續,而無需從新下載,即下載時須要通知服務器的起始位置。若是容許多線程進行分片下載,必須提供起始-截止位置。說到底就是能夠選擇下載某個片斷,整個文件的字節流,能夠截取流的片斷,也能實現流的累積,最終完成文件下載。服務器

1、原理

在 HTTP/1.1裏新增的一個頭屬性:Range,也是如今衆多號稱多線程下載工具(如 FlashGet、迅雷等)實現多線程下載的核心所在。老版本的HTTP協議不支持,因此一些老的服務器還不支持斷點續傳。多線程

Range(請求參數)工具

用於請求頭中,指定第一個字節的位置和最後一個字節的位置,通常格式:this

Range:(unit=first byte pos)-[last byte pos] url

例如:Range:100-199,取文件流的100至199之間的字節。spa

Range:100,取位置爲100後的全部字節。若是range 爲正值,服務器應該開始發送從指定的 range 參數到 HTTP 實體中數據的末尾之間的數據。線程

Range:-99,取開始的100個字節。若是range 爲負值,服務器應該開始發送從 HTTP 實體中數據的開頭到指定的 range 參數之間的數據。3d

Content-Range (響應參數)code

用於響應頭,指定整個實體中的一部分的插入位置,他也指示了整個實體的長度。在服務器向客戶返回一個部分響應,它必須描述響應覆蓋的範圍和整個實體長度。

通常格式:   

Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth] 

例如:Content-Range: bytes 1024000-1126399/7421120

HTTP協議:http://www.w3.org/Protocols/rfc2616/rfc2616.html

2、C#中實現

在C#中使用AddRange方法向請求添加指定範圍的字節範圍標頭

System.Net.HttpWebRequest

全部的方法:

經常使用的方法爲例:

void AddRange(long from, long to);
指定範圍起始、終止位置,來請求該片斷的數據。
複製代碼
        // 摘要:
        //     向請求添加指定範圍的字節範圍標頭。
        //
        // 參數:
        //   from:
        //     開始發送數據的位置。
        //
        //   to:
        //     中止發送數據的位置。
        public void AddRange(long from, long to);
複製代碼

 

咱們經過該方法,基於HTTP協議實現了斷點續傳,支持暫停、繼續下載功能,爲了更清晰顯示效果,提供了進度條顯示。效果圖:

 

Request

 請求的Range參數,咱們能夠清晰看到起具體值,這就是請求的片斷。在這裏能夠清晰的看到HTTP協議版本、請求方法、請求地址等信息

Response

服務器根據請求的Range參數,只返回該片斷的數據。咱們能夠清晰看到Content-Range的具體值。

注意:返回的StatusCode變爲了PartialContent,說明是部分數據。

代碼 含義
200 OK     請求成功返回
206 Partial Content     部分數據

 下載的核心代碼:

 

複製代碼
        /// <summary>
        /// 下載
        /// </summary>
        public void Download()
        {
            //從0計數,須要減一
            long from = this.currentSize;
            if (from < 0)
            {
                from = 0;
            }

            long to = this.currentSize + this.step - 1;
            if (to >= this.totalSize && this.totalSize > 0)
            {
                to = this.totalSize - 1;
            }
            this.Download(from, to);
        }
        /// <summary>
        /// 下載
        /// </summary>
        /// <param name="url"></param>
        /// <param name="range"></param>
        public void Download(long from,long to)
        {
            if (this.totalSize == 0)
            {
                GetTotalSize();
            }
            if (from >= this.totalSize || this.currentSize >= this.totalSize)
            {
                this.isFinished = true;
                return;
            }

            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            //request.Method = "GET";            
            request.AddRange("bytes", from, to);

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            string result = string.Empty;
            if (response != null)
            {
                byte[] buffer = this.Buffer;
                using (Stream stream = response.GetResponseStream())
                {
                    int readTotalSize = 0;
                    int size = stream.Read(buffer, 0, buffer.Length);
                    while (size > 0)
                    {
                        //只將讀出的字節寫入文件
                        fs.Write(buffer, 0, size);
                        readTotalSize += size;
                        size = stream.Read(buffer, 0, buffer.Length);
                    }

                    //更新當前進度
                    this.currentSize += readTotalSize;

                    //若是返回的response頭中Content-Range值爲空,說明服務器不支持Range屬性,不支持斷點續傳,返回的是全部數據
                    if (response.Headers["Content-Range"] == null)
                    {
                        this.isFinished = true;
                    }
                }
            }
        }
複製代碼

  

項目源碼:

http://files.cnblogs.com/yank/DownloadSample.rar

3、多線程下載

上述例子提供了簡單的斷點續傳功能,若是想再進一步實現多線程下載。原理很簡單,咱們只需根據所要下載的文件大小,進行分塊,沒塊啓動一個線程進行下載,線程只負責下載本身負責的片斷,必定要嚴格設置Range的值。具體實現這裏再也不介紹,若有興趣,下來能夠繼續研究。

相關文章
相關標籤/搜索