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

ASP.NET 實現大文件斷點續傳下載:多線程與 Range 請求完整實現

     編輯:關於C語言

前言:斷點續傳的核心原理

HTTP 協議的 Range 標頭是實現斷點續傳的關鍵。客戶端(如下載工具、瀏覽器)在請求中發送 Range: bytes=開始位置-結束位置,服務器響應 206 Partial Content 狀態碼,並通過 Content-Range 標頭指示返回的字節範圍,配合 Content-Length 指明本次傳輸長度。服務端需支持隨機讀取文件流(FileStream.Seek),並分塊輸出以避免大文件佔用過多內存。

完整實現代碼 (C# ASP.NET)

public void DownloadWithResume(string filepath)
{
    // 1. 參數準備
    const int BufferSize = 10240; // 10KB 緩衝區
    byte[] buffer = new byte[BufferSize];
    
    string filename = System.IO.Path.GetFileName(filepath);
    long dataToRead = 0;
    long startPos = 0;

    try
    {
        // 2. 打開文件流 (共享讀取模式,允許其他進程同時讀取)
        using (FileStream iStream = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            dataToRead = iStream.Length; // 文件總大小

            // 3. 處理 Range 請求
            string rangeHeader = Request.Headers["Range"];
            if (!string.IsNullOrEmpty(rangeHeader))
            {
                // 解析 "bytes=開始-結束" 或 "bytes=開始-"
                string range = rangeHeader.Replace("bytes=", "").Replace("-", "");
                if (long.TryParse(range, out long parsedPos))
                {
                    startPos = parsedPos;
                }
            }

            // 4. 設置響應標頭
            Response.Clear();
            Response.ContentType = "application/octet-stream";
            
            // 文件名編碼處理,兼容中文文件名
            string encodedFilename = HttpUtility.UrlEncode(filename, Request.ContentEncoding);
            Response.AddHeader("Content-Disposition", $"attachment; filename={encodedFilename}");

            if (startPos > 0)
            {
                // 斷點續傳響應
                Response.StatusCode = 206; // Partial Content
                Response.AddHeader("Content-Range", $"bytes {startPos}-{dataToRead - 1}/{dataToRead}");
                dataToRead -= startPos; // 剩餘待讀取字節數
                Response.AddHeader("Content-Length", dataToRead.ToString());
                
                // 定位文件流位置
                iStream.Position = startPos;
            }
            else
            {
                // 完整下載響應
                Response.AddHeader("Content-Length", dataToRead.ToString());
            }

            // 5. 分塊讀取並輸出 (核心循環)
            while (dataToRead > 0)
            {
                // 檢查客戶端是否仍連接,防止斷開後服務端繼續無意義傳輸
                if (!Response.IsClientConnected)
                {
                    dataToRead = -1; // 跳出循環
                    break;
                }

                // 讀取數據到緩衝區
                int length = iStream.Read(buffer, 0, BufferSize);
                if (length == 0) break; // 讀取完畢

                // 寫入輸出流並立即刷新到客戶端
                Response.OutputStream.Write(buffer, 0, length);
                Response.Flush();

                dataToRead -= length;
                
                // 重置緩衝區 (可選,為下一次讀取做準備)
                // buffer = new byte[BufferSize]; // 實際上 Read 會覆蓋,無需重新 new
            }
        }
    }
    catch (Exception ex)
    {
        Response.Write($"Error: {ex.Message}");
    }
    finally
    {
        Response.End(); // 確保響應結束
    }
}

關鍵技術點深度解析

1. FileShare.Read 的重要性

new FileStream(..., FileShare.Read) 允許其他進程同時打開同一文件進行讀取。若無此標誌,文件被獨佔,會導致並發下載或文件被其他程序讀取時失敗。

2. Range 標頭解析的健壯性

實際場景中 Range 標頭可能為 bytes=100- (從 100 到結尾) 或 bytes=100-200。代碼中簡單移除 bytes= 和 - 後解析起始位置,生產環境建議使用正則表達式完整解析起止位置。

3. 響應標頭的精確設置

  • 206 Partial Content:必須設置,告知客戶端為部分內容響應
  • Content-Range:格式 bytes 起始-結束/總大小,如 bytes 10240-20479/1048576
  • Content-Length:本次實際傳輸的字節數 (總大小 - 起始位置)
  • Content-Disposition:attachment 強制瀏覽器彈出下載對話框,文件名需 URL 編碼

4. 緩衝區分塊輸出與 Response.Flush()

使用 10KB 緩衝區循環讀寫,配合 Response.Flush() 立即將數據發送給客戶端,而非緩存在服務端內存中。這對大文件下載至關重要,可防止服務端內存飆升(若一次性 ReadToEnd 或 WriteFile 會將整個文件載入內存)。

5. 客戶端斷開連接檢測

循環中檢查 Response.IsClientConnected,若客戶端取消下載或網絡中斷,立即終止循環釋放資源,避免無限循環佔用線程。

6. 異常處理與資源釋放

使用 using 語句確保 FileStream 異常時也能正確關閉。finally 中調用 Response.End() 終止響應處理管道。

進階優化方向

  • 多線程並發下載:單請求單線程受限於單線程吞吐,可結合多個 Range 請求實現多線程並發下載(需客戶端支持)
  • ETag / Last-Modified 緩存驗證:結合條件請求標頭 (If-Range, If-Match) 確保文件未變更
  • 傳輸速度限制:在循環中加入 Thread.Sleep 或 Token Bucket 算法限速
  • 大文件傳輸可靠性:記錄下載日誌,支持服務端斷點續傳狀態持久化

總結

本實現展示了 ASP.NET 中基於 HTTP Range 請求的標準斷點續傳下載模式。核心在於:正確解析 Range 標頭、設置 206 響應與 Content-Range、文件流隨機定位、分塊緩衝輸出配合 Flush、客戶端連接狀態檢測。這是文件下載服務、視頻流媒體、大數據導出等場景的基石技術。

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