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

C# 實現檔案下載器:WebClient 與多線程 UI 響應

     編輯:關於C語言

專案架構

  • WinForms UI:來源網址、目標資料夾、狀態列、開始按鈕
  • WebClient 高層 HTTP 操作
  • 獨立執行緒執行下載,避免 UI 凍結
  • 手動 Stream 讀寫實現緩衝與進度感知(示範用)

核心代碼結構

public class Form1 : Form
{
    private WebClient client = new WebClient();
    // ... 控制項聲明與 InitializeComponent 省略 ...

    private void Start_Click(object sender, EventArgs e)
    {
        Thread th = new Thread(new ThreadStart(StartDownload));
        th.Start();
    }

    private void StartDownload()
    {
        Start.Enabled = false; // 必須跨線程 Invoke,此處簡化
        string URL = srcAddress.Text;
        
        // 簡易解析檔名(實際應用建議用 Uri + Content-Disposition)
        int n = URL.LastIndexOf("?");
        string URLAddress = URL; // 若有查詢字串需移除
        string fileName = URL.Substring(n + 1, URL.Length - n - 1);
        string Dir = tarAddress.Text;
        string Path = Dir + fileName;

        try
        {
            // 預檢請求
            WebRequest myre = WebRequest.Create(URLAddress);
        }
        catch (WebException exp)
        {
            MessageBox.Show(exp.Message, "Error");
            return;
        }

        try
        {
            statusBar.Text = "開始下載檔案...";
            // 方式一:WebClient 一鍵下載(推薦生產環境)
            // client.DownloadFile(URLAddress, Path);
            
            // 方式二:手動 Stream 處理(教學示範緩衝邏輯)
            statusBar.Text = "正在接收資料...";
            Stream str = client.OpenRead(URLAddress);
            byte[] mbyte = new byte[100000]; // 100KB 緩衝
            int allmybyte = mbyte.Length;
            int startmbyte = 0;

            while (allmybyte > 0)
            {
                int m = str.Read(mbyte, startmbyte, allmybyte);
                if (m == 0) break;
                startmbyte += m;
                allmybyte -= m;
            }

            using (FileStream fstr = new FileStream(Path, FileMode.OpenOrCreate, FileAccess.Write))
                fstr.Write(mbyte, 0, startmbyte);

            str.Close();
            statusBar.Text = "下載完畢!";
        }
        catch (WebException exp)
        {
            MessageBox.Show(exp.Message, "Error");
            statusBar.Text = "";
        }
        finally
        {
            Start.Enabled = true; // 同樣需 Invoke
        }
    }
}

關鍵問題與改進

1. 跨線程 UI 更新

原代碼直接在工作線程訪問 Start.Enabled、statusBar.Text 會拋 InvalidOperationException。正確做法:

this.Invoke((Action)(() => Start.Enabled = false));
this.Invoke((Action)(() => statusBar.Text = "下載中..."));

2. 檔名解析健壯性

Uri uri = new Uri(URLAddress);
string fileName = System.IO.Path.GetFileName(uri.LocalPath);
if (string.IsNullOrEmpty(fileName)) fileName = "download_" + DateTime.Now.Ticks;

3. 進度回報

client.DownloadProgressChanged += (s, e) => 
    this.Invoke((Action)(() => progressBar.Value = e.ProgressPercentage));
client.DownloadFileCompleted += (s, e) => 
    this.Invoke((Action)(() => statusBar.Text = e.Error?.Message ?? "完成"));

4. 現代化替代:HttpClient + async/await

private async void Start_Click(object sender, EventArgs e)
{
    Start.Enabled = false;
    try 
    {
        using (var http = new HttpClient())
        using (var response = await http.GetAsync(URLAddress, HttpCompletionOption.ResponseHeadersRead))
        using (var stream = await response.Content.ReadAsStreamAsync())
        using (var fs = File.Create(Path))
        {
            var buffer = new byte[81920];
            int read;
            while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                await fs.WriteAsync(buffer, 0, read);
        }
        statusBar.Text = "下載完畢!";
    }
    catch (Exception ex) { statusBar.Text = ex.Message; }
    finally { Start.Enabled = true; }
}

總結

本文展示了從 WebClient 同步阻塞、手動 Stream 緩衝、背景執行緒 UI 分離,到現代 HttpClient + async/await 非阻塞下載的演進路徑。生產環境強烈建議採用 HttpClient 單例模式、支援斷點續傳、進度回報、取消令牌、重試策略,並妥善處理跨線程 UI 更新。

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