WebClient.CancelAsync —文件仍在下载 [英] WebClient.CancelAsync — File still downloading

查看:86
本文介绍了WebClient.CancelAsync —文件仍在下载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Asp.NET Core创建一个Web API,该API公开了启动和取消长文件下载的路由.服务器应该能够同时处理多个下载.

I'm trying to create a Web API using Asp.NET Core that exposes routes to start and cancel long downloads of large files. The server should be able to handle multiple downloads at the same time.

  • 使用WebClient.DownloadFileAsync执行下载,以缩短响应时间,并返回downloadId供以后使用. WebClient的实例作为值存储在静态字典中,其对应的key自然是downloadId.
  • 对于通过访问与downloadId键对应的字典的值而检索到的客户端实例,应该使用WebClient.CancelAsync取消下载.
  • The download is performed using WebClient.DownloadFileAsync in order to have a short response time and returning a downloadId for later use. The instance of the WebClient is stored as value in a static dictionary whose corresponding key is of naturally the downloadId.
  • The download should be canceled using WebClient.CancelAsync on the instance of the client retrieved by accessing the value of the dictionary corresponding to the downloadId key.

当下载完成而未取消时,以下代码可以完美工作;正确调用AsyncCompletedEventHandler(在这种情况下为OnDownloadFileCompleted).

The following code works perfectly when the download reaches its completion without being canceled; the AsyncCompletedEventHandler (OnDownloadFileCompleted in that case) is properly invoked.

问题:调用WebClient.CancelAsync时,文件会继续下载,并且不会立即调用OnDownloadFileCompleted. WebClient似乎要等到下载完成后才能调用处理程序.但是,在这两种情况下,属性AsyncCompletedEventArgs.Canceled均已正确设置(例如,如果确实调用了WebClient.CancelAsync,则将其设置为true.

PROBLEM: When invoking WebClient.CancelAsync, the file keeps on downloading and OnDownloadFileCompleted is not invoked right away. The WebClient seems to wait until the download finishes before invoking the handler. In both case however, the property AsyncCompletedEventArgs.Canceled is properly set (e.g to true if WebClient.CancelAsync was indeed invoked.

  • 我想念什么吗?
  • 是否有更好/更智能的模式来处理WebAPI中的多次下载?

任何帮助将不胜感激!

DownloadController.cs

[Route ("api/download")]
public class DownloadController {

    private readonly DownloadService service;

    public DownloadController (DownloadService service) {
        this.service = service;
    }

    [Route ("start")]
    [HttpPost]
    public string Start ([FromForm] string fileUrl) => this.service.StartDownload (fileUrl);

    [Route ("cancel")]
    [HttpPost]
    public void Cancel ([FromForm] string downloadId) => this.service.CancelDownload (downloadId);
}

DownloadService.cs

public class DownloadService {
    public string DOWNLOAD_FOLDER { get => "C:\\tmp"; }
    public static Dictionary<string, WebClient> DownloadClients = new Dictionary<string, WebClient> ();

    public string StartDownload (string fileUrl) {
        var downloadId = Guid.NewGuid ().ToString ("N");
        DownloadClients[downloadId] = new WebClient ();
        DownloadClients[downloadId].DownloadFileCompleted += OnDownloadFileCompleted;
        DownloadClients[downloadId].DownloadFileAsync (new Uri (fileUrl), Path.Combine (DOWNLOAD_FOLDER, downloadId), downloadId);
        return downloadId;
    }

    public void CancelDownload (string downloadId) {
        if (DownloadClients.TryGetValue (downloadId, out WebClient client)) {
            client.CancelAsync ();
        }
    }

    private void OnDownloadFileCompleted (object sender, AsyncCompletedEventArgs e) {
        var downloadId = e.UserState.ToString ();
        if (!e.Cancelled) {
            Debug.WriteLine ("Completed");
        } else {
            Debug.WriteLine ("Cancelled"); //will only be reached when the file finishes downloading
        }

        if (DownloadClients.ContainsKey (downloadId)) {
            DownloadClients[downloadId].Dispose ();
            DownloadClients.Remove (downloadId);
        }
    }
}

推荐答案

我能够复制您看到的内容:CancelAsync实际上并没有取消下载.

I was able to replicate what you saw: CancelAsync does not actually cancel the download.

使用HttpClient,您可以使用CopyToAsync获取流并将其保存到文件中,并接受CancellationToken.取消令牌会立即停止下载.

Using HttpClient, you can get the stream and save it to a file using CopyToAsync, accepts a CancellationToken. Cancelling the token stops the download immediately.

这是我修改为使用HttpClientDownloadService类.

Here is the DownloadService class that I modified to use HttpClient.

public class DownloadService {
    public string DOWNLOAD_FOLDER {
        get => "C:\\tmp";
    }

    public static readonly ConcurrentDictionary<string, Download> Downloads = new ConcurrentDictionary<string, Download>();

    public async Task<string> StartDownload(string fileUrl) {
        var downloadId = Guid.NewGuid().ToString("N");
        Downloads[downloadId] = new Download(fileUrl);
        await Downloads[downloadId].Start(Path.Combine(DOWNLOAD_FOLDER, downloadId));

        return downloadId;
    }

    public void CancelDownload(string downloadId) {
        if (Downloads.TryRemove(downloadId, out var download)) {
            download.Cancel();
        }
    }

这使用了一个如下所示的Download类:

This uses a Download class that looks like this:

public class Download {
    private static readonly HttpClient Client = new HttpClient();
    private readonly string _fileUrl;

    private readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
    private Task _copyTask;
    private Stream _responseStream;
    private Stream _fileStream;

    public Download(string fileUrl) {
        _fileUrl = fileUrl;
    }

    public async Task Start(string saveTo) {
        var response = await Client.GetAsync(_fileUrl, HttpCompletionOption.ResponseHeadersRead);
        _responseStream = await response.Content.ReadAsStreamAsync();
        _fileStream = File.Create(saveTo);
        _copyTask = _responseStream.CopyToAsync(_fileStream, 81920, _tokenSource.Token).ContinueWith(task => {
            if (task.IsCanceled) return;
            _responseStream.Dispose();
            _fileStream.Dispose();
        });
    }

    public void Cancel() {
        _tokenSource.Cancel();
        _responseStream.Dispose();
        _fileStream.Dispose();
    }
}

您仍然需要做一些工作,以从Downloads列表中删除成功完成的下载,但我会把它留给您.

You will still have some work to do to remove successfully-completed downloads from your Downloads list, but I'll leave that with you.

这篇关于WebClient.CancelAsync —文件仍在下载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆