WebAPI StreamContent vs PushStreamContent

我正在实现一个MVC4 + WebAPI版本的BluImp jQueryfile upload,所有的工作都很好,但是我试图在下载非常大的文件( 〜2GB)时确保最佳的内存使用。

我已经阅读了Filip Woj关于PushStreamContent的文章,并且尽我所能地实现了它(删除asynchronous部分 – 也许这就是问题所在)。 当我正在运行testing和看TaskManager我没有看到太多差别内存使用明智,我试图了解如何处理响应之间的差异。

这是我的StreamContent版本:

private HttpResponseMessage DownloadContentNonChunked() { var filename = HttpContext.Current.Request["f"]; var filePath = _storageRoot + filename; if (File.Exists(filePath)) { HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK); response.Content = new StreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read)); response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = filename }; return response; } return ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, ""); } 

这里是我的PushStreamContent版本:

 public class FileDownloadStream { private readonly string _filename; public FileDownloadStream(string filePath) { _filename = filePath; } public void WriteToStream(Stream outputStream, HttpContent content, TransportContext context) { try { var buffer = new byte[4096]; using (var video = File.Open(_filename, FileMode.Open, FileAccess.Read)) { var length = (int)video.Length; var bytesRead = 1; while (length > 0 && bytesRead > 0) { bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length)); outputStream.Write(buffer, 0, bytesRead); length -= bytesRead; } } } catch (HttpException ex) { return; } finally { outputStream.Close(); } } } private HttpResponseMessage DownloadContentChunked() { var filename = HttpContext.Current.Request["f"]; var filePath = _storageRoot + filename; if (File.Exists(filePath)) { var fileDownload = new FileDownloadStream(filePath); var response = Request.CreateResponse(); response.Content = new PushStreamContent(fileDownload.WriteToStream, new MediaTypeHeaderValue("application/octet-stream")); response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = filename }; return response; } return ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, ""); } 

我的问题是为什么我没有看到两种方法之间的内存使用差异? 此外,我下载了StreamContenttypes的PDB,并可以看到缓冲区大小等参考(见下文),所以我想知道PushStreamContent在StreamContent之上做了什么。 我检查MSDN上的types信息,但文章是有点解释!

 namespace System.Net.Http { /// <summary> /// Provides HTTP content based on a stream. /// </summary> [__DynamicallyInvokable] public class StreamContent : HttpContent { private Stream content; private int bufferSize; private bool contentConsumed; private long start; private const int defaultBufferSize = 4096; /// <summary> /// Creates a new instance of the <see cref="T:System.Net.Http.StreamContent"/> class. /// </summary> /// <param name="content">The content used to initialize the <see cref="T:System.Net.Http.StreamContent"/>.</param> [__DynamicallyInvokable] [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public StreamContent(Stream content) : this(content, 4096) { } 

关于这两种方法的内存使用情况,对于StreamContent和PushStreamContent,Web API不缓冲响应。 下面的代码快照来自WebHostBufferPolicySelector。 源代码在这里

  /// <summary> /// Determines whether the host should buffer the <see cref="HttpResponseMessage"/> entity body. /// </summary> /// <param name="response">The <see cref="HttpResponseMessage"/>response for which to determine /// whether host output buffering should be used for the response entity body.</param> /// <returns><c>true</c> if buffering should be used; otherwise a streamed response should be used.</returns> public virtual bool UseBufferedOutputStream(HttpResponseMessage response) { if (response == null) { throw Error.ArgumentNull("response"); } // Any HttpContent that knows its length is presumably already buffered internally. HttpContent content = response.Content; if (content != null) { long? contentLength = content.Headers.ContentLength; if (contentLength.HasValue && contentLength.Value >= 0) { return false; } // Content length is null or -1 (meaning not known). // Buffer any HttpContent except StreamContent and PushStreamContent return !(content is StreamContent || content is PushStreamContent); } return false; } 

PushStreamContent也适用于需要将数据“推送”到数据stream的场景,StreamContent从数据stream中“拉出”数据。 因此,对于当前下载文件的情况,使用StreamContent应该没问题。

下面的例子:

 // Here when the response is being written out the data is pulled from the file to the destination(network) stream response.Content = new StreamContent(File.OpenRead(filePath)); // Here we create a push stream content so that we can use XDocument.Save to push data to the destination(network) stream XDocument xDoc = XDocument.Load("Sample.xml", LoadOptions.None); PushStreamContent xDocContent = new PushStreamContent( (stream, content, context) => { // After save we close the stream to signal that we are done writing. xDoc.Save(stream); stream.Close(); }, "application/xml");