6

Odata プロトコルを使用して WebAPI 経由でコンテンツを一括エクスポートする必要があります。PushStreamContent を使用して、結果をデータベースから直接ストリーミングしようとしています。ローカル IIS インスタンスでサービスを実行するとうまく動作しますが、これをサーバーにプッシュすると、データがストリーミングされ、最後の 2 キロバイトで一時停止して「ハング」します。

ファイルサイズを追跡することでそれを確認しました。ローカルで実行すると 9094KB のファイルが取得され、同じコードをサーバーにデプロイすると 9092KB になり、接続が開いたままになり、転送が停止します。クライアントを強制終了してファイルを見ると、ストリームであった json が書き込みの途中で切断されていることがわかります。さらに、IIS で開いている接続を調べて、接続がまだアクティブであることを確認できます。

とにかく、PushStreamContent がデータの送信を停止し、ストリームを閉じないように見えるのはなぜでしょうか? エラーが発生した場合、ストリームと接続は閉じられます。

public HttpResponseMessage GetBulkExport(ODataQueryOptions<vwBulkExport> options)
{

    var reportData = options.ApplyTo(dbContext.vwBulkExport, new ODataQuerySettings() { EnsureStableOrdering = false });

    return new ResponseStreamer(Request).StreamAsync(reportData);

}



public class ResponseStreamer
{

    private HttpRequestMessage request;

    public ResponseStreamer(HttpRequestMessage request)
    {
        this.request = request;
    }

    public HttpResponseMessage StreamAsync(IQueryable data)
    {
        HttpResponseMessage response = request.CreateResponse();
        response.Content = new PushStreamContent(
          async (outputStream, httpContent, transportContext) =>
         {
             try
             {
                 int counter = 0;
                 foreach (var item in data)
                 {
                     counter++;

                     string json = JsonConvert.SerializeObject(item);
                     var buffer = Encoding.UTF8.GetBytes(json);
                     await outputStream.WriteAsync(buffer, 0, buffer.Length);

                     if (counter == 10)
                     {
                         counter = 0;
                         await outputStream.FlushAsync();
                     }
                 }
             }
             finally
             {

                 await outputStream.FlushAsync();
                 outputStream.Close();
                 outputStream.Dispose();
             }
         });


        return response;
    }

}

これが私のクライアントコードです

    using (var writer = File.OpenWrite("C:\\temp\\" + Guid.NewGuid().ToString()))
    {
        var client = new RestClient("http://localhost");
        var url =  "/odata/BulkExport";
        var request = new RestRequest(url);
        request.AddHeader("authorization", string.Format("Bearer {0}", authToken));

        request.ResponseWriter = (responseStream) => responseStream.CopyTo(writer);
        var response = client.DownloadData(request);
    }

アップデート

私は広範なテストを行ってきましたが、ストリームが閉じられない (したがって、最後のチャンクが送信されない) ことが起こっていると思います。上記のデータ反復を次のように変更することで、この結論に達しました。

for (int count = 0; count < 1000; count++) //foreach (var item in data)
{

    string json = JsonConvert.SerializeObject(count.ToString()) + Environment.NewLine;
    var buffer = Encoding.Default.GetBytes(json);
    await outputStream.WriteAsync(buffer, 0, buffer.Length);
}

私が見ているのは、600の「行」しか返らないということです。ここでも 2 KB が不足しているように見えました。次に、ループをに変更し、count <601ストリーム全体が転送されましたが、ストリームは決して閉じません。私が考えているのは、内部バッファのサイズが 4K 前後 (出力された 0 ~ 600 の数字) であり、ストリームが閉じていないため、最後の数バイトが受信されないことです。それは理にかなっていますか?

とにかく、なぜストリームが閉じないのでしょうか? 最終的にそれを持っていますが、スローされたエラーは見られません。

アップデート

より多くの情報を明らかにしました。HTTP 1.1 仕様では、チャンク ストリームは長さ 0 のチャンクで終了する必要があると規定されています。少し掘り下げた後、それが起こっているはずであることがわかりましたが、何らかの理由でそうではありません。私のクライアントでは、Connection: Keep-Aliveヘッダーを削除して置き換えましたConnection: Closeが、同じ問題があります、(テストアプリをシャットダウンして) 接続を強制的に閉じるとすぐに、最後の数バイトがディスクに書き込まれ、すべて問題ありません。これが、長さゼロのチャンクが送信されていないことを知る方法です。

だから今、質問はなりました。ストリームを閉じたときに最後の長さ 0 のチャンクが送信されないのはなぜですか? 私が読んだことから、呼び出しHttpContext.Current.ApplicationInstance.CompleteRequest();はリクエストを強制的に終了させ、そのチャンクを書き出す必要があります。これを finally ブロックの最後の行として追加し、デバッガーを使用して実行中であることを確認しました。ただし、そのチャンクはまだ設定されていません。

これらはすべて、IIS でホストされている私の開発マシンでは機能しますが、Web サーバーでは機能しないことを覚えておいてください。

私のマシンはWindows 10を実行しており、asp.net 5がインストールされており、IIS7はすべてデフォルトです。

WebサーバーはWindowsサーバーです(バージョンはわかりません)IIS8を実行していますが、asp.net 4.5しかインストールされていません。私の最初の予感は、これが問題であるということでした.asp.netフレームワークのバージョンが異なりますが、確認したところ、プロジェクトはASP.NET 4.5のみを対象としています。私はまだサーバーを更新しようとしていますが、4.5 をターゲットにしているので、何の役にも立たないと思います。

4

1 に答える 1

2

問題は .net フレームワークのバグによるものだと思います。サーバーを.net 4.6に更新した後、動作しています。

適用したパッチはこちらです。https://www.microsoft.com/en-us/download/details.aspx?id=48137

于 2016-02-11T17:25:04.713 に答える