我已经下载了并行编程团队的最新示例,但我没有成功添加取消下载文件的可能性。
以下是我结束的代码:
var wreq = (HttpWebRequest)WebRequest.Create(uri);
// Fire start event
DownloadStarted(this, new DownloadStartedEventArgs(remoteFilePath));
long totalBytes = 0;
wreq.DownloadDataInFileAsync(tmpLocalFile,
cancellationTokenSource.Token,
allowResume,
totalBytesAction =>
{
totalBytes = totalBytesAction;
},
readBytes =>
{
Log.Debug("Progression : {0} / {1} => {2}%", readBytes, totalBytes, 100 * (double)readBytes / totalBytes);
DownloadProgress(this, new DownloadProgressEventArgs(remoteFilePath, readBytes, totalBytes, (int)(100 * readBytes / totalBytes)));
})
.ContinueWith( (antecedent ) =>
{
if (antecedent.IsFaulted)
Log.Debug(antecedent.Exception.Message);
//Fire end event
SetEndDownload(antecedent.IsCanceled, antecedent.Exception, tmpLocalFile, 0);
}, cancellationTokenSource.Token);
我想在下载完成后触发结束事件,因此是ContinueWith。
我稍微更改了示例代码以添加CancellationToken和2个代表来获取要下载的文件的大小,以及下载的进度:
return webRequest.GetResponseAsync()
.ContinueWith(response =>
{
if (totalBytesAction != null)
totalBytesAction(response.Result.ContentLength);
response.Result.GetResponseStream().WriteAllBytesAsync(filePath, ct, resumeDownload, progressAction).Wait(ct);
}, ct);
我不得不将调用添加到Wait函数中,因为如果我不这样做,则该方法退出并且结束事件过早触发。
以下是修改后的方法扩展(很多代码,道歉:p)
public static Task WriteAllBytesAsync(this Stream stream, string filePath, CancellationToken ct, bool resumeDownload = false, Action<long> progressAction = null)
{
if (stream == null) throw new ArgumentNullException("stream");
// Copy from the source stream to the memory stream and return the copied data
return stream.CopyStreamToFileAsync(filePath, ct, resumeDownload, progressAction);
}
public static Task CopyStreamToFileAsync(this Stream source, string destinationPath, CancellationToken ct, bool resumeDownload = false, Action<long> progressAction = null)
{
if (source == null) throw new ArgumentNullException("source");
if (destinationPath == null) throw new ArgumentNullException("destinationPath");
// Open the output file for writing
var destinationStream = FileAsync.OpenWrite(destinationPath);
// Copy the source to the destination stream, then close the output file.
return CopyStreamToStreamAsync(source, destinationStream, ct, progressAction).ContinueWith(t =>
{
var e = t.Exception;
destinationStream.Close();
if (e != null)
throw e;
}, ct, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Current);
}
public static Task CopyStreamToStreamAsync(this Stream source, Stream destination, CancellationToken ct, Action<long> progressAction = null)
{
if (source == null) throw new ArgumentNullException("source");
if (destination == null) throw new ArgumentNullException("destination");
return Task.Factory.Iterate(CopyStreamIterator(source, destination, ct, progressAction));
}
private static IEnumerable<Task> CopyStreamIterator(Stream input, Stream output, CancellationToken ct, Action<long> progressAction = null)
{
// Create two buffers. One will be used for the current read operation and one for the current
// write operation. We'll continually swap back and forth between them.
byte[][] buffers = new byte[2][] { new byte[BUFFER_SIZE], new byte[BUFFER_SIZE] };
int filledBufferNum = 0;
Task writeTask = null;
int readBytes = 0;
// Until there's no more data to be read or cancellation
while (true)
{
ct.ThrowIfCancellationRequested();
// Read from the input asynchronously
var readTask = input.ReadAsync(buffers[filledBufferNum], 0, buffers[filledBufferNum].Length);
// If we have no pending write operations, just yield until the read operation has
// completed. If we have both a pending read and a pending write, yield until both the read
// and the write have completed.
yield return writeTask == null
? readTask
: Task.Factory.ContinueWhenAll(new[]
{
readTask,
writeTask
},
tasks => tasks.PropagateExceptions());
// If no data was read, nothing more to do.
if (readTask.Result <= 0)
break;
readBytes += readTask.Result;
if (progressAction != null)
progressAction(readBytes);
// Otherwise, write the written data out to the file
writeTask = output.WriteAsync(buffers[filledBufferNum], 0, readTask.Result);
// Swap buffers
filledBufferNum ^= 1;
}
}
基本上,在被调用方法链的末尾,如果已请求取消,我让CancellationToken抛出OperationCanceledException。
我希望在吸引人的代码中获取IsFaulted == true并使用已取消的标志和正确的异常触发结束事件。
但我得到的是行上未处理的异常
response.Result.GetResponseStream()。WriteAllBytesAsync(filePath,ct,resumeDownload,progressAction).Wait(ct);
告诉我我没有捕获到AggregateException。我尝试了各种各样的东西,但是我没有成功地使整个过程正常工作。
你们有没有人玩过那个图书馆并且可以帮助我?
提前致谢
麦克
答案 0 :(得分:0)
这几乎是我所期望的行为。我无法完全绕过你的代码,但我怀疑如果你看一下AggregateException.InnerExceptions属性,你会在那里找到一个TaskCancelled或OperationCancelledException。取消令牌会导致取消的任务抛出异常,该任务通常由调用代码捕获,因为它等待任务完成,并且它被包装在聚合中。
以下链接有更多详细信息。
http://msdn.microsoft.com/en-us/library/dd997396.aspx
http://msdn.microsoft.com/en-us/library/dd997364.aspx
我不会写更多,因为我不是100%确信我理解你的代码。