3

次のコードが別の c# アプリケーションを呼び出してファイルをダウンロードし、ディスクに保存する .Net Web API アプリケーションを作成しています。すべてが正常に機能してファイルを取得できる場合もありますが、以下のコードでストリームを読み取ることができず、他のアプリケーションでリモート接続が閉じられたという例外が表示されることがあります。

public async Task<string> GetFilePathAsync(TestModel model)
{
    string filePath = string.Empty;
    var response = await cgRequestHelper.DownloadAsync(model);  

    if (response.IsSuccessStatusCode)
    {                    
        filePath = await SaveCgStreamAsync(cgResponse, serviceModel.FileName);
    }
    return filePath;
}

public async Task<HttpResponseMessage> DownloadAsync(TestModel model)
{            
    if (model == null)
        throw new ArgumentNullException("model");
    if (string.IsNullOrEmpty(model.Url))
        throw new ArgumentNullException("Url");
    if (model.Headers == null)
        throw new ArgumentNullException("Headers");

    HttpResponseMessage response;
    using (HttpClient httpClient = new HttpClient())
    {
        foreach (var header in model.Headers)
        {
            httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
        }
        response = await httpClient.GetAsync(model.Url, HttpCompletionOption.ResponseHeadersRead); 
    }
    return response;            
}        

public async Task<string> SaveCgStreamAsync(HttpResponseMessage response, string fileName)
{
    if (response == null)
        throw new ArgumentNullException("response");
    if (string.IsNullOrEmpty(fileName))
        throw new ArgumentNullException("fileName");

    var filePath = _CreateTemporaryLocation(fileName);
    Stream cgStream = null;
    Stream fileStream = null;
    try
    {
        cgStream =  await response.Content.ReadAsStreamAsync();
        fileStream = File.Open(filePath, FileMode.Create);
        await cgStream.CopyToAsync(fileStream);
    }
    catch(Exception e)
    {
        throw;
    }
    finally
    {
        if(cgStream != null)
            cgStream.Dispose();
        if(fileStream != null)
            fileStream.Dispose();
    }

    return filePath;            
}

Global.asax.cs で ServicePointManager.DefaultConnectionLimit = 1000 を設定しました

私の現在のアプリケーションでは、「await cgStream.CopyToAsync(fileStream);」で例外が発生しています。cgStream を読み取ろうとすると行が表示されます。例外は、「閉じられたストリームにアクセスできません」です。with null InnerException もう 1 つのアプリケーション例外は次のとおりです。 System.Web.HttpException: リモート ホストが接続を閉じました。エラー コードは 0x800703E3 です。System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError (Int32 結果、ブール値 throwOnDisconnect) で System.Web.Hosting.IIS7WorkerRequest.ExplicitFlush() で System.Web.HttpResponse.Flush (ブール値 finalFlush、ブール値 async)

平均して、10 件中 1 件のリクエストが上記のエラーで失敗しています。これはランダムであり、常に失敗するとは限らないため、問題のトラブルシューティングは非常に困難です。

上記のコードはhttp://www.tugberkugurlu.com/archive/effectively-streaming-large-http-responses-with-httpclientの助けを借りて書かれています。

この問題に関連するヘルプは大歓迎です!

ありがとう

4

3 に答える 3

8

コードの問題を見つけました: 問題は、HttpClient オブジェクトを「using」で初期化し、その応答を using スコープ外で使用していたことです。これにより HttpClient オブジェクトが破棄されるため、リモート接続が切断されるため、コンテンツをストリーミングできません。ランダムな動作は、オブジェクトがいつ破棄されるかわからないためです。ストリーミングの前に破棄されない場合もあれば、破棄される場合もあります。

于 2016-10-31T21:40:12.370 に答える
0

代わりに試しStream.CopyToてみますが、以前に以下のコードを試していて、後でそれを変更したことをお知らせするためにCopyToAsync

public async Task<string> GetFilePathAsync(TestModel model)
    {
        string filePath = string.Empty;
        var response = await cgRequestHelper.DownloadAsync(model);  

        if (response.IsSuccessStatusCode)
        {                   
            Stream cgStream = await response.Content.ReadAsStreamAsync();
            filePath = SaveCgStream(cgStream , model.FileName);
        }
        return filePath;
    }

    public string SaveCgStream(Stream cgStream, string fileName)
        {
            if (response == null)
                throw new ArgumentNullException("response");
            if (string.IsNullOrEmpty(fileName))
                throw new ArgumentNullException("fileName");

            const int CHUNK_SIZE = 40000;
            var filePath = _CreateTemporaryLocation(fileName);
            var fileStream = File.Create(filePath, CHUNK_SIZE);

            int bytesRead;
            var buffer = new byte[CHUNK_SIZE];
            bool isReadStarted = false;
            try
            {
                do
                {
                    bytesRead = cgStream.Read(buffer, 0, CHUNK_SIZE);
                    if (bytesRead > 0)
                    {
                        isReadStarted = true;
                        fileStream.Write(buffer, 0, bytesRead);
                    }
                } while (bytesRead > 0);
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                fileStream.Close();
            }
            return filePath;
        }

そして、私はbytesRead = cgStream.Read(buffer, 0, CHUNK_SIZE);行で同じエラーを受けていました。それでも問題だと思いますCopyToAsyncか?ブレークポイントを置くと、CopyToAsyncメソッドに行く前に Read プロパティが false であるため、ストリームが読み取れないことがわかるので、私はそれを疑っています。

CopyTo代わりに試してみましたがCopyToAsync、役に立ちませんでした。スクリーンショット:

ここに画像の説明を入力

于 2016-10-31T18:49:11.373 に答える
0

ドキュメントによるStream.CopyToAsyncと、まあ、非同期です。これは、呼び出し元 (メソッド) をブロックしないことを意味し、finally ブロックでストリームを破棄します。言い換えれば、競合状態があります。に切り替えると、準備完了Stream.CopyToです。

于 2016-10-31T18:38:14.267 に答える