C# WebAPI StreamContent 与 PushStreamContent
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16168683/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WebAPI StreamContent vs PushStreamContent
提问by bUKaneer
I'm implementing a MVC4 + WebAPI version of the BluImp jQuery File Uploadall works well with my initial attempt but Im trying to ensure the best use of memory whilst downloading very large files (~2GB).
我正在实现一个MVC4 + WebAPI 版本的 BluImp jQuery 文件上传在我最初的尝试中运行良好,但我试图确保在下载非常大的文件 (~2GB) 时最好地使用内存。
I've read Filip Woj's article on PushStreamContentand implemented it as best I can (removing the async parts - perhaps this is the problem?). When Im running tests and watching TaskManager Im not seeing much difference memory usage wise and Im trying to understand the difference between how the responses are handled.
我已经阅读了Filip Woj 关于 PushStreamContent 的文章,并尽我所能实现了它(删除异步部分 - 也许这就是问题所在?)。当我运行测试并观看 TaskManager 时,我没有看到太多的内存使用差异,我试图了解响应处理方式之间的差异。
Here's my StreamContent version:
这是我的 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, "");
}
And here's my PushStreamContent version:
这是我的 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, "");
}
My question is why am I not seeing much difference in memory usage between the two approaches? Additionally Ive downloaded the PDB for the StreamContent type and can see references to buffer sizes and such forth (see below) so I'd like to know exactly what PushStreamContent is doing above and beyond StreamContent. Ive check the Type info on MSDN but the article were a little light on explanation!
我的问题是为什么我没有看到两种方法在内存使用方面有太大差异?此外,我已经下载了 StreamContent 类型的 PDB,并且可以看到对缓冲区大小等的引用(见下文),所以我想确切地知道 PushStreamContent 在 StreamContent 之上和之外正在做什么。我检查了 MSDN 上的类型信息,但这篇文章的解释有点少!
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)
{
}
采纳答案by Kiran Challa
Regarding the memory usage of these both approaches, for StreamContent and PushStreamContent, Web API doesn't buffer the responses. Following snapshot of code is from WebHostBufferPolicySelector. Source code here.
关于这两种方法的内存使用情况,对于 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;
}
Also PushStreamContent is for scenarios where you need to 'push' data to the stream, where as StreamContent 'pulls' data from the stream. So, for your current scenario of downloading files, using StreamContent should be fine.
PushStreamContent 也适用于需要将数据“推送”到流的场景,而 StreamContent 从流中“拉出”数据。因此,对于您当前下载文件的情况,使用 StreamContent 应该没问题。
Examples below:
下面的例子:
// 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");

