Как я могу отменить и приостановить/возобновить асинхронную загрузку веб — клиента?

#c# #winforms #webclient

Вопрос:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using unfreez_wrapper;

namespace Downloads
{
    public partial class Form1 : Form
    {
        List<string> urls = new List<string>();
        DownloadProgressTracker tracker;
        long myLong = 0;
        WebClient client;

        public Form1()
        {
            InitializeComponent();

            tracker = new DownloadProgressTracker(50, TimeSpan.FromMilliseconds(500));

            lblDownloadProgress.Text = "";
            lblStatus.Text = "Download Pending";
            lblAmount.Text = "";
            lblSpeed.Text = "";
            urls.Add("https://speed.hetzner.de/10GB.bin");
            urls.Add("https://speed.hetzner.de/100MB.bin");
        }

        private async Task DownloadAsync()
        {
            using (var client = new WebClient())
            {
                this.client = client;

                client.DownloadFileCompleted  = (s, e) =>
                {
                    if (e.Cancelled)
                    {
                        client.Dispose();
                        return;
                    }
                };

                client.DownloadFileCompleted  = (s, e) => lblStatus.Text = "Download File Completed.";
                client.DownloadProgressChanged  = (s, e) => tracker.SetProgress(e.BytesReceived, e.TotalBytesToReceive);
                client.DownloadProgressChanged  = (s, e) => lblAmount.Text = SizeSuffix(e.BytesReceived)   "/"   SizeSuffix(e.TotalBytesToReceive);
                client.DownloadProgressChanged  = (s, e) =>  lblSpeed.Text = tracker.GetBytesPerSecondString();
                client.DownloadProgressChanged  = (s, e) => myLong = Convert.ToInt64(client.ResponseHeaders["Content-Length"]);
                client.DownloadProgressChanged  = (s, e) => progressBar1.Value = e.ProgressPercentage;
                client.DownloadProgressChanged  = (s, e) =>
                {
                    lblDownloadProgress.Text = "%"   e.ProgressPercentage.ToString();
                    lblDownloadProgress.Left = Math.Min(
                        (int)(progressBar1.Left   e.ProgressPercentage / 100f * progressBar1.Width),
                        progressBar1.Width - lblDownloadProgress.Width
                    );
                };

                for (int i = 0; i < urls.Count; i  )
                {
                    tracker.NewFile();

                    await client.DownloadFileTaskAsync(new Uri(urls[i]), @"d:satImagesimg"   i   ".gif");
                }
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private async void btnStart_Click(object sender, EventArgs e)
        {
            lblStatus.Text = "Downloading...";
            await DownloadAsync();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            if (client != null)
            {
                client.CancelAsync();
                lblStatus.Text = "Download Stopped";
            }
        }

        static readonly string[] SizeSuffixes =
                   { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
        static string SizeSuffix(Int64 value, int decimalPlaces = 1)
        {
            if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
            if (value < 0) { return "-"   SizeSuffix(-value, decimalPlaces); }
            if (value == 0) { return string.Format("{0:n"   decimalPlaces   "} bytes", 0); }

            // mag is 0 for bytes, 1 for KB, 2, for MB, etc.
            int mag = (int)Math.Log(value, 1024);

            // 1L << (mag * 10) == 2 ^ (10 * mag) 
            // [i.e. the number of bytes in the unit corresponding to mag]
            decimal adjustedSize = (decimal)value / (1L << (mag * 10));

            // make adjustment when the value is large enough that
            // it would round up to 1000 or more
            if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
            {
                mag  = 1;
                adjustedSize /= 1024;
            }

            return string.Format("{0:n"   decimalPlaces   "} {1}",
                adjustedSize,
                SizeSuffixes[mag]);
        }
    }
}
 

Я не пробовал часть паузы/возобновления, только отмену.

Вверху я добавил клиент с именем глобальной переменной WebClient :

 WebClient client;
 

В методе загрузки я ссылаюсь на клиента с клиентом в методе :
А также проверка, отменен ли и утилизирован ли клиент в методе загрузки :

 this.client = client;
    
                    client.DownloadFileCompleted  = (s, e) =>
                    {
                        if (e.Cancelled)
                        {
                            client.Dispose();
                            return;
                        }
                    };
 

В событии btnStop click я добавил этот код :

 if (client != null)
                {
                    client.CancelAsync();
                    lblStatus.Text = "Download Stopped";
                }
 

Когда он загружается, и я нажимаю кнопку «Стоп», я получаю это сообщение об исключении :

Cacelled получает исключение

Комментарии:

1. Когда вы читаете «Мы не рекомендуем вам использовать класс WebClient для новой разработки». в документации, как вы это интерпретируете?

Ответ №1:

Я считаю, что вам нужно обернуть транзакции WebClient в объект BackgroundWorker, чтобы вы могли установить для WorkerSupportsCancellation значение true перед отменой. Тогда отмена должна работать без исключений, я считаю.

Пожалуйста, обратитесь —

https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=net-5.0