覆盖WebHostBufferPolicySelector以进行非缓冲文件上载

在尝试创建非缓冲文件上载时,我扩展了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 upload执行其他操作。 因此我使用HttpContent方法ReadAsMultiPartAsync(),这似乎是内存增长的来源。 我已经在“await”之前/之后放置了跟踪输出,并且看起来当任务阻塞时,内存使用量会相当快地增加。

一旦我在ReadAsMultiPartAsync()返回的部分中找到文件内容,我就使用Stream.CopyTo()将文件内容写入磁盘。 这按预期写入磁盘,但不幸的是,此时源文件已经在内存中。

有没有人对可能出现的问题有任何想法? 似乎ReadAsMultiPartAsync()正在缓冲整个post数据; 如果这是真的,为什么我们需要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(); //  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); } } 

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,则请求是无缓冲的。

但是, ReadAsMultipartAsync()会将所有内容加载到MemoryStream – 因为如果您未指定提供程序,则默认为MultipartMemoryStreamProvider。

要在处理每个部分时将文件自动保存到磁盘,请使用MultipartFormDataStreamProvider (如果处理文件和表单数据)或MultipartFileStreamProvider (如果只处理文件)。

asp.net或这里有一个例子。 在这些示例中,所有内容都发生在控制器中,但没有理由不在格式化程序中使用它。

另一个选择,如果你真的想要使用流,那就是实现一个从MultipartStreamProviderinheritance的自定义类,它会在抓取部分流时触发你想要的任何处理。 用法与上述提供程序类似 – 您需要将其传递给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 } 

但当然,由于缺乏更好的词,“贫民窟”。