2017-07-02 23 views
0

我在寫一個UWP(WinRT)解決方案,從服務器下載文件並保存到光盤,同時指示進度。爲此,我擴展了IAsyncOperationWithProgress方法。取消IInputStream.ReadAsync如果連接到主機丟失

我的問題是,在一個單行:

while ((await responseStream.ReadAsync(streamReadBuffer, bufferLength, InputStreamOptions.None)).Length > 0 && !token.IsCancellationRequested) 

我等內容被從流中讀取。如果連接丟失,該線路將無限期地等待,直到連接重新建立。

對於UWP,我是如何爲Http請求分配超時值,或者是取消單個任務?

我的擴展IAsyncOperationWithProgress:

private static IAsyncOperationWithProgress<HttpDownloadStatus, DownloadResponse> DownloadAsyncWithProgress(this HttpClient client, HttpRequestMessage request, CancellationToken cancelToken, StorageFile fileToStore) 
    { 
     const uint bufferLength = 2048; 
     var progressResponse = new DownloadResponse 
     { 
      File = fileToStore, 
      DownloadStatus = HttpDownloadStatus.Started, 
      BytesRecieved = 0, 
      Progress = 0.00 
     }; 
     string result = string.Empty; 
     HttpDownloadStatus returnStatus = HttpDownloadStatus.Busy; 
     IBuffer streamReadBuffer = new Windows.Storage.Streams.Buffer(bufferLength); 

     var operation = client.SendRequestAsync(request, HttpCompletionOption.ResponseHeadersRead); 

      return AsyncInfo.Run<HttpDownloadStatus, DownloadResponse>((token, progress) => 
       Task.Run(async() => 
       { 
        try 
        { 
         if (cancelToken != CancellationToken.None) token = cancelToken; 
         HttpResponseMessage respMessage; 
         try 
         { 
          respMessage = await operation; 
         } 
         catch (Exception ex) 
         { 
          throw new Exception("Error sending download request - " + ex.Message); 
         } 
         progressResponse.TotalBytes = Convert.ToInt64(respMessage.Content.Headers.ContentLength); 
         using (var responseStream = await respMessage.Content.ReadAsInputStreamAsync()) 
         { 
          using (var fileWriteStream = await fileToStore.OpenAsync(FileAccessMode.ReadWrite)) 
          { 
           token.ThrowIfCancellationRequested(); 
           while ((await responseStream.ReadAsync(streamReadBuffer, bufferLength, InputStreamOptions.None)).Length > 0 && !token.IsCancellationRequested) 
           { 

            while(DownloadManager.ShouldPauseDownload && DownloadManager.CurrentDownloadingBook.FileName == fileToStore.Name) 
            { 
             if (token.IsCancellationRequested) 
              break; 
            } 

            progressResponse.DownloadStatus = HttpDownloadStatus.Busy; 
            if (token.IsCancellationRequested) 
            { 
             // progressResponse.DownloadStatus = HttpDownloadStatus.Cancelled; 
             // returnStatus = HttpDownloadStatus.Cancelled; 
             break; 
            } 

            ; 
            await fileWriteStream.WriteAsync(streamReadBuffer); 
            progressResponse.BytesRecieved += (int)streamReadBuffer.Length; 
            progressResponse.Progress = (progressResponse.BytesRecieved/(double)progressResponse.TotalBytes) * 100; 
            //Only give response when close to a byte 
            if (progressResponse.BytesRecieved % 1048576 == 0) 
            { 
             Debug.WriteLine("Should be 1 meg: " + progressResponse.BytesRecieved); 
             progress.Report(progressResponse); 
            } 
           } //while (offset < contentLength); 
           if (token.IsCancellationRequested) 
           { 
            progressResponse.DownloadStatus = HttpDownloadStatus.Cancelled; 
            returnStatus = HttpDownloadStatus.Cancelled; 
           } 

          } 
         } 
         if(returnStatus == HttpDownloadStatus.Busy) //only set it if it was still legitimately busy 
          returnStatus = HttpDownloadStatus.Complete; 

         return returnStatus; 
        } 
        catch (TaskCanceledException tce) 
        { 
         Debug.WriteLine("CANCEL - Download cancellation token caught from within task"); 
         return HttpDownloadStatus.Cancelled; 
        } 
       }, token)); 


    } 

如何上面的代碼被稱爲:

HttpClient httpClient = new HttpClient(PFilter); 

try 
{ 
DownloadResponse downloadResponse = new DownloadResponse { File = fileToSave, DownloadStatus = HttpDownloadStatus.Busy }; 
CancellationToken cancelToken = m_CancellationSource.Token; 

HttpRequestMessage requestMsg = new HttpRequestMessage(HttpMethod.Get, downloadUri); 

IAsyncOperationWithProgress<HttpDownloadStatus,DownloadResponse> operationWithProgress = httpClient.DownloadAsyncWithProgress(requestMsg, cancelToken, fileToSave); 

operationWithProgress.Progress = new AsyncOperationProgressHandler<HttpDownloadStatus, DownloadResponse>((result, progress) => { progressDelegate(progress); }); 

var response = await operationWithProgress; 

downloadResponse.DownloadStatus = response; 

if (response == HttpDownloadStatus.Complete) 
{ 
    return HttpWebExceptionResult.Success; 
} 
else if (response == HttpDownloadStatus.ConnectionLost) 
    return HttpWebExceptionResult.ConnectionFailure; 
else if (response == HttpDownloadStatus.Cancelled) 
    return HttpWebExceptionResult.RequestCanceled; 
else 
    return HttpWebExceptionResult.UnexpectedError; 



} 
catch (TaskCanceledException tce) 
{ 
    Debug.WriteLine("CANCEL - token caught from StartDownloadAsync "); 
    return HttpWebExceptionResult.RequestCanceled; 
} 
catch (Exception ex) 
{ 
    return HttpWebExceptionResult.UnexpectedError; 
} 
+0

我總是發現把我的邏輯放在純'async' /'await'方法中然後有一個單獨的'AsyncOperation'包裝器方法會更清潔。 –

+0

您能否詳細說明一下w.r.t.上面的例子?你會如何分開包裝AsyncOperation? –

+1

我的意思是讓所有的邏輯使用更自然的'async' /'await',這將導致一個(私有的)Task DownloadWithProgressAsync(IProgress )',然後有一個(public)'IAsyncOperationWithProgress DownloadAsyncWithProgress'只是包裝另一種方法。 –

回答

0

如果你想從的WinRT API取消IAsyncOperation操作,你需要將其轉換爲Task首先,然後提供一個CancellationToken,這將在超時時間後過期。如果它沒有在2秒內完成

在此示例中,inputStream.ReadAsync()將被取消:

var timeoutCancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(2)); 
var response = await inputStream.ReadAsync(buffer, bufferLength, InputStreamOptions.None).AsTask(timeoutCancellationSource.Token); 

如果你不想等待2秒,你可以在你想要的CancellationTokenSource隨時打電話Cancel()

timeoutCancellationSource.Cancel();