5

バッファリングされていないファイルのアップロードを作成しようとして、System.Web.Http.WebHost.WebHostBufferPolicySelector を拡張し、この記事で説明されているように関数 UseBufferedInputStream() をオーバーライドしました: http://www.strathweb.com/2012/09/dealing -with-large-files-in-asp-net-web-api/ . ファイルがコントローラーに POST されると、トレース出力で、上書きされた関数 UseBufferedInputStream() が期待どおり確実に FALSE を返していることがわかります。ただし、診断ツールを使用すると、ファイルがアップロードされるにつれてメモリが増加することがわかります。

私のカスタム MediaTypeFormatter (FileMediaFormatter のようなもの: http://lonetechie.com/ ) で大量のメモリ使用量が発生しているようです。受信ファイルをインクリメンタルにディスクに書き込みたいのはこのフォーマッタですが、json を解析し、Content-Type:multipart/form-data アップロードで他の操作を行う必要もあります。したがって、私は HttpContent メソッド ReadAsMultiPartAsync() を使用していますが、これがメモリ増加の原因のようです。「待機」の前後にトレース出力を配置しましたが、タスクがブロックされている間、メモリ使用量がかなり急速に増加しているようです。

ReadAsMultiPartAsync() によって返されたパーツでファイルの内容を見つけたら、ファイルの内容をディスクに書き込むために Stream.CopyTo() を使用しています。これは期待どおりにディスクに書き込みますが、残念ながら、ソース ファイルはこの時点で既にメモリ内にあります。

何がうまくいかないのか考えている人はいますか?ReadAsMultiPartAsync() が投稿データ全体をバッファリングしているようです。それが本当なら、ファイルの内容を取得するために var fileStream = await fileContent.ReadAsStreamAsync() が必要なのはなぜですか? パーツをメモリに読み込まずに分割する別の方法はありますか? 私の MediaTypeFormatter のコードは次のようになります。

// save the stream so we can seek/read again later
Stream stream = await content.ReadAsStreamAsync();  

var parts = await content.ReadAsMultipartAsync(); // <- memory usage grows rapidly

if (!content.IsMimeMultipartContent())
{
    throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);               
}

//
// pull data out of parts.Contents, process json, etc.
//

// find the file data in the multipart contents
var fileContent = parts.Contents.FirstOrDefault(
x => x.Headers.ContentDisposition.DispositionType.ToLower().Trim() == "form-data" && 
x.Headers.ContentDisposition.Name.ToLower().Trim() == "\"" + DATA_CONTENT_DISPOSITION_NAME_FILE_CONTENTS + "\"");

// write the file to disk
using (var fileStream = await fileContent.ReadAsStreamAsync())
{
    using (FileStream toDisk = File.OpenWrite("myUploadedFile.bin"))
    {
        ((Stream)fileStream).CopyTo(toDisk);
    }
}
4

1 に答える 1

11

WebHostBufferPolicySelector基になるリクエストがバッファレスかどうかのみを指定します。これは、内部で Web API が行うことです。

IHostBufferPolicySelector policySelector = _bufferPolicySelector.Value;
bool isInputBuffered = policySelector == null ? true : policySelector.UseBufferedInputStream(httpContextBase);
    Stream inputStream = isInputBuffered
                  ? requestBase.InputStream
          : httpContextBase.ApplicationInstance.Request.GetBufferlessInputStream();

したがって、実装が false を返す場合、リクエストはバッファレスです。

ただし、プロバイダーを指定しない場合、デフォルトで MultipartMemoryStreamProvider になるため、ReadAsMultipartAsync()すべてを - にロードします。MemoryStream

すべてのパーツが処理されるときにファイルを自動的にディスクに保存するには、MultipartFormDataStreamProvider (ファイルとフォーム データを処理する場合) またはMultipartFileStreamProvider (ファイルのみを処理する場合) を使用します。

asp.netまたはhereに例があります。これらの例では、すべてがコントローラーで行われますが、フォーマッターなどで使用しない理由はありません。

もう 1 つのオプションは、本当にストリームを操作したい場合、MultipartStreamProviderから継承するカスタム クラスを実装することです。このクラスは、ストリームの一部を取得するとすぐに必要な処理を開始します。ReadAsMultipartAsync(provider)使用法は前述のプロバイダーと同様です。メソッドに渡す必要があります。

最後に - 自殺願望がある場合 - 基礎となるリクエスト ストリームは理論的にはバッファレスであるため、コントローラーまたはフォーマッターで次のようなものを使用できます。

            Stream stream = HttpContext.Current.Request.GetBufferlessInputStream();
            byte[] b = new byte[32*1024];
            while ((n = stream.Read(b, 0, b.Length)) > 0)
            {
                //do stuff with stream bit
            }

しかし、もちろん、「ゲットー」というより適切な言葉がないため、それは非常に重要です。

于 2013-02-16T04:21:25.463 に答える