WriteToStreamAsync取消不起作用

时间:2016-03-31 19:47:34

标签: c# win-universal-app .net-4.5 filestream windows-10-universal

我正在运行一个Task,它从一个流复制到另一个流。这没有问题,包括进度报告。但我无法取消任务。如果我触发CancellationToken,复制进度会一直运行直到完成,然后任务被取消,但这当然是迟到的。这是我的实际代码

private async Task Download(Uri uriToWork, CancellationToken cts)
{
    HttpClient httpClient = new HttpClient();
    HttpRequestMessage requestAction = new HttpRequestMessage();
    requestAction.Method = new HttpMethod("GET");
    requestAction.RequestUri = uriToWork;

    HttpResponseMessage httpResponseContent = await httpClient.SendRequestAsync(requestAction, HttpCompletionOption.ResponseHeadersRead);

    using (Stream streamToRead = (await httpResponseContent.Content.ReadAsInputStreamAsync()).AsStreamForRead())
    {
        string fileToWrite = Path.GetTempFileName();
        using (Stream streamToWrite = File.Open(fileToWrite, FileMode.Create))
        {
            await httpResponseContent.Content.WriteToStreamAsync(streamToWrite.AsOutputStream()).AsTask(cts, progressDownload); 

            await streamToWrite.FlushAsync();
            //streamToWrite.Dispose();
         }

         await streamToRead.FlushAsync();
         //streamToRead.Dispose();
    }
    httpClient.Dispose();
}

有人可以帮助我,或者可以解释一下,为什么它不起作用?

2 个答案:

答案 0 :(得分:0)

这个操作一直持续到完成吗?

await httpResponseContent.Content.WriteToStreamAsync(streamToWrite.AsOutputStream()).AsTask(cts, progressDownload); 

或者是这个吗?

await streamToWrite.FlushAsync();

我认为后者可能也需要具有CancellationToken:

await streamToWrite.FlushAsync(cts);

不幸的是我无法回答为什么没有取消这种取消。但是,以流块形式编写的解决方案可能有所帮助。

以下是非常脏的东西:

private async Task Download(Uri uriToWork, CancellationToken cts) {

    using(HttpClient httpClient = new HttpClient()) {

        HttpRequestMessage requestAction = new HttpRequestMessage();
        requestAction.Method = new HttpMethod("GET");
        requestAction.RequestUri = uriToWork;

        HttpResponseMessage httpResponseContent = await httpClient.SendRequestAsync(requestAction, HttpCompletionOption.ResponseHeadersRead);
        string fileToWrite = Path.GetTempFileName();
        using(Stream streamToWrite = File.Open(fileToWrite, FileMode.Create)) {

            // Disposes streamToWrite to force any write operation to fail
            cts.Register(() => streamToWrite.Dispose());

            try {
                await httpResponseContent.Content.WriteToStreamAsync(streamToWrite.AsOutputStream()).AsTask(cts, p);
            }
            catch(TaskCanceledException) {
                return; // "gracefully" exit when the token is cancelled
            }

            await streamToWrite.FlushAsync();
        }
    }
}
  • 我将httpClient括在using中,以便return正确处理它。
  • 我删除了根本未使用的streamToRead
  • 现在这里是恐怖:我添加了一个在取消令牌时执行的委托:它在写入(ughhhh)时处置streamToWrite,当WriteToStreamAsync无法再写入时触发TaskCancelledException在这条流水中。

请不要向我扔一个呕吐袋,我在这个“通用”框架中没有足够的经验,这个框架看起来与通常的不同。

这是一个看起来更容易接受的分块流解决方案。我缩短了原始代码并添加了IProgress作为参数。

async Task Download(Uri uriToWork, CancellationToken cts, IProgress<int> progress) {
    using(HttpClient httpClient = new HttpClient()) {

        var chunkSize = 1024;
        var buffer = new byte[chunkSize];
        int count = 0;
        string fileToWrite = Path.GetTempFileName();

        using(var inputStream = await httpClient.GetInputStreamAsync(uriToWork)) {
            using(var streamToRead = inputStream.AsStreamForRead()) {
                using(Stream streamToWrite = File.OpenWrite(fileToWrite)) {
                    int size;
                    while((size = await streamToRead.ReadAsync(buffer, 0, chunkSize, cts).ConfigureAwait(false)) > 0) {
                        count += size;
                        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => progress.Report(count));
                        // progress.Report(count);
                        await streamToWrite.WriteAsync(buffer, 0, size, cts).ConfigureAwait(false);
                    }
                }
            }
        }
    }
}

答案 1 :(得分:0)

阻塞操作很可能不是WriteToStreamAsync()而是FlushAsync(),因此@Larry的假设应该正确,FlushAsync方法也需要取消令牌。