2017-07-02 16 views
0

サーバーからファイルをダウンロードし、進行状況を示すディスクに保存するUWP(WinRT)ソリューションを作成しています。この目的のために、IAsyncOperationWithProgressメソッドを拡張しました。ホストへの接続が失われた場合、IInputStream.ReadAsyncをキャンセルします。

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

私は、ストリームから読み込まれるコンテンツを待つ:

私の問題は、単一の行であることです。接続が失われた場合、この回線は接続が再確立されるまで無期限に待機します。

UWPの場合、Httpリクエストにタイムアウトを割り当てるか、または1つのタスクをキャンセルする方法はありますか。

マイ拡張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 )'になり、次に '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(); 
関連する問題