我正在运行一个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();
}
有人可以帮助我,或者可以解释一下,为什么它不起作用?
答案 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
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
方法也需要取消令牌。