WriteToStreamAsync 取消不起作用
WriteToStreamAsync cancel does not work
我是运行一个任务,它从一个流复制到另一个流。这没有问题,包括进度报告。但是我不能取消任务。如果我触发 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();
}
有人可以帮助我,或者可以解释为什么它不起作用吗?
这个操作是否一直持续到完成?
await httpResponseContent.Content.WriteToStreamAsync(streamToWrite.AsOutputStream()).AsTask(cts, progressDownload);
还是这一个?
await streamToWrite.FlushAsync();
我认为后者可能也需要有 CancellationToken:
await streamToWrite.FlushAsync(cts);
不幸的是,我无法回答为什么没有发生这种取消。但是,将 Stream 写入块 的解决方案可能会有所帮助。
这里有一些非常脏的东西:
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
不再在此处理的流中写入。
请不要向我扔呕吐袋,我对这个 "Universal" 框架没有足够的经验,它看起来与通常的框架非常不同。
这里有一个chunked stream的方案,看起来更容易接受。我稍微缩短了原始代码并添加了 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);
}
}
}
}
}
}
阻塞操作很可能不是WriteToStreamAsync()
而是FlushAsync()
,所以@Larry的假设应该是正确的,FlushAsync
方法也需要取消令牌。
我是运行一个任务,它从一个流复制到另一个流。这没有问题,包括进度报告。但是我不能取消任务。如果我触发 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();
}
有人可以帮助我,或者可以解释为什么它不起作用吗?
这个操作是否一直持续到完成?
await httpResponseContent.Content.WriteToStreamAsync(streamToWrite.AsOutputStream()).AsTask(cts, progressDownload);
还是这一个?
await streamToWrite.FlushAsync();
我认为后者可能也需要有 CancellationToken:
await streamToWrite.FlushAsync(cts);
不幸的是,我无法回答为什么没有发生这种取消。但是,将 Stream 写入块 的解决方案可能会有所帮助。
这里有一些非常脏的东西:
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
不再在此处理的流中写入。
请不要向我扔呕吐袋,我对这个 "Universal" 框架没有足够的经验,它看起来与通常的框架非常不同。
这里有一个chunked stream的方案,看起来更容易接受。我稍微缩短了原始代码并添加了 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);
}
}
}
}
}
}
阻塞操作很可能不是WriteToStreamAsync()
而是FlushAsync()
,所以@Larry的假设应该是正确的,FlushAsync
方法也需要取消令牌。