多線程下載工具(如網路螞蟻、IDM)之所以快,核心在於多線程分段下載與斷點續傳。本文將以 C# 為例,示範如何使用 HttpWebRequest/HttpWebResponse 實作支援多線程分段下載的檔案傳輸程式。
HTTP/1.1 定義 Range 標頭,允許客戶端請求資源的特定位元組範圍。伺服器回應 206 Partial Content 並傳回指定區段資料。此為多線程下載的基石。
// 請求第 100~1000 位元組
request.AddRange(100, 1000);
public class HttpFile
{
private readonly Form1 _form;
private readonly int _threadIndex;
public HttpFile(Form1 form, int threadIndex)
{
_form = form;
_threadIndex = threadIndex;
}
public void Receive()
{
string url = _form.Url;
string tempFile = _form.TempFiles[_threadIndex];
long start = _form.StartPositions[_threadIndex];
long length = _form.Lengths[_threadIndex];
_form.Log($"Thread {_threadIndex} start: {start} - {start + length}");
using (var fs = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None))
using (var request = (HttpWebRequest)WebRequest.Create(url))
{
request.AddRange(start, start + length - 1);
request.Method = "GET";
request.Timeout = 30000;
using (var response = (HttpWebResponse)request.GetResponse())
using (var stream = response.GetResponseStream())
{
byte[] buffer = new byte[8192];
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
fs.Write(buffer, 0, read);
}
}
}
_form.MarkThreadCompleted(_threadIndex);
_form.Log($"Thread {_threadIndex} completed.");
}
}
public partial class Form1 : Form
{
public string Url { get; set; }
public string[] TempFiles { get; set; }
public long[] StartPositions { get; set; }
public long[] Lengths { get; set; }
public bool[] ThreadCompleted { get; set; }
public int ThreadCount { get; set; }
private void btnStart_Click(object sender, EventArgs e)
{
Url = txtUrl.Text.Trim();
ThreadCount = int.Parse(txtThreadCount.Text);
// 1. 取得檔案總大小
long totalSize;
using (var request = (HttpWebRequest)WebRequest.Create(Url))
using (var response = (HttpWebResponse)request.GetResponse())
{
totalSize = response.ContentLength;
}
// 2. 計算分段
long partSize = totalSize / ThreadCount;
TempFiles = new string[ThreadCount];
StartPositions = new long[ThreadCount];
Lengths = new long[ThreadCount];
ThreadCompleted = new bool[ThreadCount];
for (int i = 0; i < ThreadCount; i++)
{
TempFiles[i] = $"{i}.part";
StartPositions[i] = i * partSize;
Lengths[i] = (i == ThreadCount - 1) ? totalSize - StartPositions[i] : partSize;
}
// 3. 啟動下載執行緒
var threads = new Thread[ThreadCount];
for (int i = 0; i < ThreadCount; i++)
{
var httpFile = new HttpFile(this, i);
threads[i] = new Thread(httpFile.Receive);
threads[i].Start();
}
// 4. 啟動合併執行緒
new Thread(MergeFiles).Start();
}
private void MergeFiles()
{
// 等待所有下載執行緒完成
while (ThreadCompleted.Any(c => !c))
Thread.Sleep(100);
using (var fs = new FileStream(txtSavePath.Text, FileMode.Create, FileAccess.Write))
{
foreach (string part in TempFiles)
{
using (var partStream = new FileStream(part, FileMode.Open, FileAccess.Read))
{
partStream.CopyTo(fs);
}
File.Delete(part); // 清理暫存檔
}
}
Invoke(new Action(() =>
{
txtEndTime.Text = DateTime.Now.ToString();
MessageBox.Show("下載完成!");
}));
}
public void MarkThreadCompleted(int index) => ThreadCompleted[index] = true;
public void Log(string msg) => Invoke(new Action(() => listBoxLog.Items.Add(msg)));
}
request.AddRange(start, end) 指定下載區間。bool[] 旗標陣列配合輪詢等待,或改用 CountdownEvent、Task.WhenAll 更優雅。.NET Core / .NET 5+ 建議使用 HttpClient 搭配 System.IO.Pipelines 或 Task 非同步模式,程式碼更簡潔且效能更佳:
public async Task DownloadRangeAsync(HttpClient client, string url, long start, long length, Stream destination)
{
using var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Range = new RangeHeaderValue(start, start + length - 1);
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
await stream.CopyToAsync(destination);
}
本文示範了傳統 HttpWebRequest 實作多線程下載的完整流程,涵蓋分段計算、執行緒協調、暫存檔合併等關鍵步驟。雖然現代開發推薦使用 HttpClient 非同步模式,但理解底層 HTTP Range 機制與執行緒同步原理,對於維護舊專案或設計高效能下載引擎仍具參考價值。