2009-12-11 26 views
0

我已經下載了並行編程小組的最後一個示例,並且我沒有成功正確添加取消下載一個文件。如何在新的並行編程庫的示例中下載文件時添加正確的取消文件

這裏是我最終有代碼:

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(文件路徑,CT,resumeDownload,progressAction).Wait(CT)未處理的異常。

告訴我,我沒有發現AggregateException。我嘗試了各種各樣的東西,但我沒有成功地讓整個事情正常工作。

你們有沒有玩過這個圖書館,可以幫助我嗎?

在此先感謝

邁克

回答

0

這是一個很值得我期望的行爲。我無法完全掌握你的代碼,但是我懷疑如果你看看AggregateException.InnerExceptions屬性,你會發現一個TaskCancelled或OperationCancelledException。取消令牌會導致被取消的任務拋出的異常,通常由調用代碼捕獲,因爲它會等待任務完成,並將其包含在聚合中。

以下鏈接有更多詳細信息。

http://msdn.microsoft.com/en-us/library/dd997396.aspx

http://msdn.microsoft.com/en-us/library/dd997364.aspx

我不打算多寫,因爲我不是100%地確信我理解你的代碼。

相關問題