.NET Framework 提供三層網路存取抽象:WebRequest/WebResponse(協議不可知的請求/回應基礎架構)與 WebClient(高階便利封裝)。理解其關係與適用場景,能寫出更健壯的下載/上傳程式。
| 類別 | 層級 | 核心職責 | 典型子類 |
|---|---|---|---|
WebRequest | 抽象基類 | 建立協議特定請求(HTTP、FTP、FILE) | HttpWebRequest, FileWebRequest, FtpWebRequest |
WebResponse | 抽象基類 | 封裝伺服器回應串流 | HttpWebResponse, FileWebResponse |
WebClient | 高階元件 | 同步/非同步 下載上傳字串、檔案、位元組陣列 | 無(密封類別) |
依 URI Scheme 自動回傳對應具體子類:https:// → HttpWebRequest,file:// → FileWebRequest。可透過 WebRequest.RegisterPrefix 擴充自訂協議。
回傳 WebResponse,需強制轉型存取協議特定屬性(如 HttpWebResponse.StatusCode)。
內部自動建立請求、開啟串流、寫入檔案,支援 DownloadFileAsync 非同步版本與 DownloadProgressChanged 進度事件。
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Forms;
public class HttpDownloader
{
public string FetchHtml(string url)
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = CredentialCache.DefaultCredentials;
request.UserAgent = "Mozilla/5.0 (compatible; .NET Downloader)";
using (var response = (HttpWebResponse)request.GetResponse())
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
public void SaveHtml(string url, string filePath)
{
using (var client = new WebClient())
{
client.Encoding = Encoding.UTF8;
client.DownloadFile(url, filePath); // 同步阻塞
// 非同步版:client.DownloadFileAsync(new Uri(url), filePath);
}
}
}
// UI 使用範例
private void btnFetch_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtUrl.Text)) return;
try { rtbHtml.Text = new HttpDownloader().FetchHtml(txtUrl.Text.Trim()); }
catch (Exception ex) { MessageBox.Show("錯誤:" + ex.Message); }
}
private void btnSave_Click(object sender, EventArgs e)
{
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
new HttpDownloader().SaveHtml(txtUrl.Text.Trim(), saveFileDialog.FileName);
MessageBox.Show("儲存完成");
}
}
public void WriteToFileShare(string fileUri, string content, int timeoutMs = 5000)
{
var uri = new Uri(fileUri); // 格式:file://server/share/path.txt
var request = (FileWebRequest)WebRequest.CreateDefault(uri);
request.Method = "POST"; // 或 "PUT"
request.Timeout = timeoutMs;
using (var stream = request.GetRequestStream())
using (var writer = new StreamWriter(stream, Encoding.UTF8))
{
writer.Write(content);
}
// 注意:FileWebRequest 不提供 GetResponse,寫入成功即無例外
}
AllowAutoRedirect = true(預設)CookieContainer = new CookieContainer() 維持 SessionAutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.DeflateKeepAlive = true 重用連線ReadWriteTimeout 與 Timeout 分開設定| 限制 | 說明 | 現代替代 |
|---|---|---|
| 無法自訂 HTTP Header(Referer、Authorization 等) | 需繼承覆寫 GetWebRequest | HttpClient 原生支援 |
| 同步方法阻塞 UI 執行緒 | 務必用 DownloadFileTaskAsync | HttpClient.GetStreamAsync |
| 不支援 HTTP/2、gRPC | 僅限 HTTP/1.1 | HttpClient 完整支援 |
| 密封類別,難以單元測試 | 無介面可 Mock | IHttpClientFactory + HttpClient |
.NET Core / .NET 5+ 專案**強烈建議直接使用 HttpClient**;唯有維護 .NET Framework 4.x 老專案且無法引入 System.Net.Http 時,才繼續使用 WebClient/HttpWebRequest。
// 現代標準寫法
private static readonly HttpClient http = new HttpClient();
public async Task DownloadStringAsync(string url) => await http.GetStringAsync(url);
public async Task DownloadFileAsync(string url, string path)
{
using var stream = await http.GetStreamAsync(url);
using var fs = File.Create(path);
await stream.CopyToAsync(fs);
}