You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WinForm SSH.NET SFTP下载进度条不更新问题排查

SFTP下载进度条无法更新的问题

我是C#新手,表述不当之处请见谅。我尝试为SFTP下载功能实现进度条,但始终无法正常更新。参考了相关示例但没有用Task.Run(() => Download());,因为需要等待下载完成后再执行后续操作。

疑问点

  • 操作哪里有误?该如何解决?
  • 为什么progressbar1的Value值无法更新?

调试时发现代码会停在DownloadProgresBar方法中的progressBar1.Invoke((MethodInvoker)delegate { progressBar1.Value = (int)bytesRead; });这一行,但进度条无变化,而Console.WriteLine("Downloaded:" + bytesRead);却能正常输出。

我的代码

private void Download(string updateDataLoc, int updateDataLocPort, string updatUsr, string updatPas, string RemPath, string LocPath, string fichero)
{
    try
    {
        int Port = updateDataLocPort;
        string Host = updateDataLoc;
        string Username = updatUsr;
        string Password = updatPas;
        string RemotePath = RemPath; // "/remote/path/";
        string SourcePath = LocPath; // @"C:\local\path\";
        string FileName = fichero; // "download.txt";

        
        string SourceFilePath = SourcePath; //includes the file name
        using (var stream = new FileStream(SourcePath, FileMode.Create))
        using (var client = new SftpClient(Host, Port, Username, Password))
        {
            client.Connect();
            SftpFileAttributes attributes = client.GetAttributes(RemotePath + FileName);
            // Set progress bar maximum on foreground thread
            progressBar1.Invoke(
                (MethodInvoker)delegate { progressBar1.Maximum = (int)attributes.Size; });
            // Download with progress callback
            client.DownloadFile(RemotePath + FileName, stream, downloadCallback: DownloadProgresBar);
            MessageBox.Show("Download complete");
        }

    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message);
    }
}
private void DownloadProgresBar(ulong bytesRead)
{
    Console.WriteLine("Downloaded:" + bytesRead);
    // Update progress bar on foreground thread
    progressBar1.Invoke((MethodInvoker)delegate { progressBar1.Value = (int)bytesRead; });
}

问题分析与解决

核心问题:主线程被阻塞

你没把下载操作放到后台线程,导致Download方法直接在UI主线程执行。SFTP下载是耗时操作,会彻底卡住主线程——虽然你用了Invoke试图更新进度条,但主线程正忙着处理下载,根本没时间处理UI更新的消息队列,所以进度条看起来没变化,调试时会停在Invoke那一行(因为委托一直在排队等主线程空闲)。

解决方法

用async/await结合Task.Run,既能让下载在后台跑不卡UI,又能保证后续操作在下载完成后执行:

修改后的代码

// 按钮点击事件改成async,用来触发下载并等待完成
private async void btnDownload_Click(object sender, EventArgs e)
{
    // 替换成你的参数
    await Download("你的主机地址", 22, "用户名", "密码", "/远程路径/", @"C:\本地路径\", "文件名.txt");
    // 下载完成后的后续操作写在这里,比如文件校验、解压等
    MessageBox.Show("后续操作启动");
}

private async Task Download(string updateDataLoc, int updateDataLocPort, string updatUsr, string updatPas, string RemPath, string LocPath, string fichero)
{
    try
    {
        int Port = updateDataLocPort;
        string Host = updateDataLoc;
        string Username = updatUsr;
        string Password = updatPas;
        string RemotePath = RemPath;
        string SourcePath = LocPath;
        string FileName = fichero;

        // 把耗时的下载操作丢到后台线程
        await Task.Run(() =>
        {
            using (var stream = new FileStream(SourcePath, FileMode.Create))
            using (var client = new SftpClient(Host, Port, Username, Password))
            {
                client.Connect();
                SftpFileAttributes attributes = client.GetAttributes(RemotePath + FileName);
                // 初始化进度条最大值和初始值
                progressBar1.Invoke((MethodInvoker)delegate 
                { 
                    progressBar1.Maximum = (int)attributes.Size;
                    progressBar1.Value = 0;
                });
                // 下载并触发进度回调
                client.DownloadFile(RemotePath + FileName, stream, downloadCallback: DownloadProgresBar);
            }
        });
        
        MessageBox.Show("Download complete");
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message);
    }
}

private void DownloadProgresBar(ulong bytesRead)
{
    Console.WriteLine("Downloaded:" + bytesRead);
    // 用BeginInvoke异步更新,避免等待主线程拖慢下载
    progressBar1.BeginInvoke((MethodInvoker)delegate 
    { 
        // 加边界检查,防止bytesRead超过最大值导致报错
        if (bytesRead <= progressBar1.Maximum)
            progressBar1.Value = (int)bytesRead; 
    });
}

关键说明

  1. async/await + Task.Run组合:后台线程处理下载,主线程保持响应,Invoke/BeginInvoke的UI更新委托能及时执行。
  2. 进度条初始化:添加progressBar1.Value = 0,避免初始值异常导致的显示问题。
  3. 用BeginInvoke替代Invoke:Invoke会等待主线程执行完委托才返回,可能轻微拖慢下载;BeginInvoke是异步调用,不用等待,对下载性能更友好。
  4. 边界值检查:防止下载过程中bytesRead意外超过进度条最大值,引发参数异常。

内容的提问来源于stack exchange,提问作者sigsag

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 08:42:30