2017-07-02 78 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();