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

如何基于HttpClient和HttpWebRequest实现文件下载暂停恢复按钮逻辑

实现下载暂停/恢复功能

已在Form1设计器中添加btnPauseResume按钮,需求如下:

  • 按钮默认文本为“Pause”,点击后暂停当前文件下载,按钮文本切换为“Resume”
  • 再次点击按钮时,从断点处恢复下载

解决方案实现步骤

1. 新增Form1成员变量

在Form1类中添加用于跟踪下载状态的变量:

public partial class Form1 : Form
{
    // 原有变量保留,新增以下内容
    private bool isPaused = false;
    private string currentDownloadUrl;
    private long downloadedBytes = 0;
    private CancellationTokenSource pauseTokenSource;

    // 构造函数及其他原有代码不变
}

2. 修改DownloadFile方法以支持断点续传

调整方法逻辑,支持从指定字节位置开始下载,并区分暂停和整体取消的场景:

private async Task DownloadFile(string docUrl, string path, long startByte = 0)
{
    currentDownloadUrl = docUrl;
    downloadedBytes = startByte;
    var client = new HttpClient();
    var filePath = Path.Combine(path, Path.GetFileNameWithoutExtension(docUrl));
    downloadFilePath = filePath;

    Invoke((MethodInvoker)delegate
    {
        textBoxUrl.Text = docUrl;
    });

    var progress = new Progress<float>();
    var speedProgress = new Progress<Tuple<long, TimeSpan>>();
    var sizeProgress = new Progress<long>();

    progress.ProgressChanged += Progress_ProgressChanged;
    sizeProgress.ProgressChanged += SizeProgress_ProgressChanged;

    var fileMode = startByte == 0 ? FileMode.Create : FileMode.Append;
    using (downloadStream = new FileStream(filePath, fileMode, FileAccess.Write, FileShare.None))
    {
        if (startByte > 0)
        {
            downloadStream.Seek(startByte, SeekOrigin.Begin);
        }

        try
        {
            pauseTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token);
            await client.DownloadDataAsync(docUrl, downloadStream, progress, speedProgress, sizeProgress, pauseTokenSource.Token, startByte);
        }
        catch (OperationCanceledException)
        {
            if (isPaused) return;
            if (File.Exists(downloadFilePath))
            {
                File.Delete(downloadFilePath);
            }
        }
        finally
        {
            CloseFileStream();
        }
    }
}

3. 扩展HttpClientProgressExtensions支持断点续传

修改DownloadDataAsync方法,添加Range请求头实现断点续传:

public static async Task DownloadDataAsync(this HttpClient client, string requestUrl, Stream destination, IProgress<float> progress = null, IProgress<Tuple<long, TimeSpan>> speedProgress = null, IProgress<long> sizeProgress = null, CancellationToken cancellationToken = default(CancellationToken), long startByte = 0)
{
    using (var request = new HttpRequestMessage(HttpMethod.Get, requestUrl))
    {
        if (startByte > 0)
        {
            request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(startByte, null);
        }

        using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
        {
            if (startByte > 0 && response.StatusCode != System.Net.HttpStatusCode.PartialContent)
            {
                throw new InvalidOperationException("服务器不支持断点续传");
            }

            var contentLength = response.Content.Headers.ContentLength;
            var totalContentLength = startByte + (contentLength ?? 0);
            contentSize = totalContentLength;

            using (var download = await response.Content.ReadAsStreamAsync(cancellationToken))
            {
                if (progress is null || !contentLength.HasValue)
                {
                    await download.CopyToAsync(destination, cancellationToken);
                    return;
                }

                var watch = new System.Diagnostics.Stopwatch();
                watch.Start();

                var progressWrapper = new Progress<long>(bytesRead =>
                {
                    var totalBytes = startByte + bytesRead;
                    var elapsed = watch.Elapsed;
                    progress.Report(GetProgressPercentage(totalBytes, totalContentLength));

                    if (elapsed.TotalSeconds > 1)
                    {
                        var speed = (long)(bytesRead / elapsed.TotalSeconds);
                        speedProgress?.Report(Tuple.Create(totalBytes, TimeSpan.FromSeconds(speed)));
                        watch.Restart();
                    }

                    sizeProgress?.Report(totalBytes);
                });

                await download.CopyToAsync(destination, 81920, progressWrapper, cancellationToken);
            }
        }
    }

    float GetProgressPercentage(float totalBytes, float currentBytes) => (totalBytes / currentBytes) * 100f;
}

4. 实现btnPauseResume按钮点击事件

添加按钮点击逻辑,切换暂停/恢复状态:

private async void btnPauseResume_Click(object sender, EventArgs e)
{
    if (!isPaused)
    {
        isPaused = true;
        btnPauseResume.Text = "Resume";
        pauseTokenSource?.Cancel();
    }
    else
    {
        isPaused = false;
        btnPauseResume.Text = "Pause";
        await DownloadFile(currentDownloadUrl, Path.GetDirectoryName(downloadFilePath), downloadedBytes);
    }
}

5. 调整DownloadFiles方法的队列处理逻辑

确保暂停后不会终止整个下载队列:

private async Task DownloadFiles(string selectedFolder)
{
    foreach (string link in links.Distinct())
    {
        if (cancellationTokenSource.Token.IsCancellationRequested)
            break;

        if (!uniqueLinks.Contains(link))
        {
            string filePath = Path.Combine(selectedFolder, Path.GetFileNameWithoutExtension(link));

            if (!File.Exists(filePath))
            {
                try
                {
                    await DownloadFile(link, selectedFolder);
                    anyDownloadsOccurred = true;
                }
                catch (OperationCanceledException)
                {
                    if (isPaused)
                    {
                        links = links.Skip(links.IndexOf(link)).ToList();
                        break;
                    }
                }
            }
            uniqueLinks.Add(link);
        }
    }

    if (!isPaused)
    {
        DownloadComplete();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:40:00