首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 實現 HTTP 斷點續傳下載—— 原理與完整代碼

     編輯:關於C#

大文件下載斷網、崩潰後能從中斷處繼續,而非重頭開始,這是下載器的基本素質。本文深度解析 HTTP Range 請求原理、.NET 核心類(HttpWebRequest、FileStream)用法,並提供生產級可運行的斷點續傳代碼,含異常處理、進度回調、多線程擴展接口。

一、HTTP 斷點續傳核心原理

  1. 客戶端發起請求時,在 Header 加入 Range: bytes=起始位置-
  2. 服務端支持 Accept-Ranges: bytes,回應 206 Partial Content 及 Content-Range
  3. 客戶端以 FileStream.Seek 定位到文件末尾,追加寫入響應流

二、關鍵類與方法

類關鍵成員用途
HttpWebRequestAddRange(start)設置 Range 請求頭
HttpWebResponseGetResponseStream()獲取響應數據流
FileStreamSeek(offset, SeekOrigin)移動文件指針到斷點
StreamRead/Write(byte[], offset, count)塊讀寫,控制緩衝區大小

三、生產級斷點續傳核心代碼

using System;
using System.IO;
using System.Net;
using System.Threading;

public class ResumableDownloader
{
    public event Action ProgressChanged; // (已下載, 總大小)
    public event Action Completed;
    public event Action Failed;

    public void Download(string url, string filePath, CancellationToken token = default)
    {
        long startPos = 0;
        FileMode mode = FileMode.Create;

        // 1. 檢查本地文件是否存在,決定續傳位置
        if (File.Exists(filePath))
        {
            var fi = new FileInfo(filePath);
            startPos = fi.Length;
            mode = FileMode.Append;
        }

        try
        {
            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";
            request.Timeout = 30000;
            request.ReadWriteTimeout = 60000;

            // 2. 設置 Range:從 startPos 續傳
            if (startPos > 0)
                request.AddRange(startPos);

            using (var response = (HttpWebResponse)request.GetResponse())
            using (var respStream = response.GetResponseStream())
            using (var fileStream = new FileStream(filePath, mode, FileAccess.Write, FileShare.None, 8192))
            {
                long totalSize = startPos + response.ContentLength; // 總大小 = 已有 + 本次響應
                byte[] buffer = new byte[8192];
                int bytesRead;
                long downloaded = startPos;

                while ((bytesRead = respStream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    token.ThrowIfCancellationRequested();

                    fileStream.Write(buffer, 0, bytesRead);
                    downloaded += bytesRead;

                    ProgressChanged?.Invoke(downloaded, totalSize);
                }

                fileStream.Flush();
            }

            Completed?.Invoke();
        }
        catch (OperationCanceledException)
        {
            // 用戶取消,保留已下載部分
        }
        catch (Exception ex)
        {
            Failed?.Invoke(ex);
        }
    }
}

四、控制台測試程序

class Program
{
    static void Main()
    {
        string url = "https://speed.hetzner.de/100MB.bin"; // 測試用大文件
        string path = @"C:\Downloads\100MB.bin";

        var dl = new ResumableDownloader();
        dl.ProgressChanged += (done, total) =>
            Console.Write($"\r進度: {done * 100.0 / total:F1}% ({done}/{total} bytes)");
        dl.Completed += () => Console.WriteLine("\n下載完成!");
        dl.Failed += ex => Console.WriteLine($"\n失敗: {ex.Message}");

        dl.Download(url, path);
        Console.ReadLine();
    }
}

五、進階擴展方向

  1. 多線程分塊下載:將文件分為 N 段,並發請求 Range: bytes=start-end,合併時注意順序。
  2. 速度限制:Thread.Sleep 或 TokenBucket 算法控制寫入頻率。
  3. 校驗完整性:下載完成後對比 MD5/SHA256 或 ETag。
  4. 現代替代:.NET Core 2.1+ 推薦 HttpClient + HttpCompletionOption.ResponseHeadersRead,支持 Range、自動解壓、HTTP/2。

六、常見坑點速查

現象原因解決
服務端回 200 而非 206不支持 Range 或範圍無效檢查 Accept-Ranges、重試全量下載
續傳後文件損壞文件已被修改(ETag 變化)下載前比對 Last-Modified/ETag
大文件 > 2GB 失敗int 溢出全程用 long、AddRange(long) 重載
網絡抖動頻繁異常無重試機制引入 Polly 重試策略

七、總結

斷點續傳本質是「HTTP Range + 文件隨機寫入」。掌握 HttpWebRequest.AddRange、FileStream.Seek、流式讀寫三大核心,即可構建健壯下載器。現代專案建議遷移至 HttpClient,享受更優的連接池、HTTP/2、取消令牌等原生支持。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved