WebAPI StreamContent 与 PushStreamContent [英] WebAPI StreamContent vs PushStreamContent

查看:29
本文介绍了WebAPI StreamContent 与 PushStreamContent的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在实施 MVC4 + WebAPI 版本BluImp jQuery 文件上传在我最初的尝试中运行良好,但我试图确保在下载非常大的文件(~2GB)的同时充分利用内存.

I'm implementing a MVC4 + WebAPI version of the BluImp jQuery File Upload all works well with my initial attempt but Im trying to ensure the best use of memory whilst downloading very large files (~2GB).

我已阅读 FilipWoj 关于 PushStreamContent 的文章并尽我所能实现它(删除异步部分 - 也许这就是问题所在?).当我运行测试并观看 TaskManager 时,我没有看到内存使用情况有多大差异,我试图了解响应处理方式之间的差异.

I've read Filip Woj's article on PushStreamContent and 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.

这是我的 StreamContent 版本:

Here's my StreamContent version:

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 版本:

And here's my PushStreamContent version:

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, "");
}

我的问题是为什么我没有看到两种方法之间的内存使用差异很大?此外,我已经下载了 StreamContent 类型的 PDB,并且可以看到对缓冲区大小等的引用(见下文),所以我想确切地知道 PushStreamContent 在 StreamContent 之上和之外正在做什么.我已经在 MSDN 上查看了类型信息,但这篇文章的解释有点少!

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!

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.源代码此处.

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.

    /// <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 适用于您需要将数据推送"到流的场景,而 StreamContent 从流中拉取"数据.因此,对于您当前下载文件的场景,使用 StreamContent 应该没问题.

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.

以下示例:

// 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");

这篇关于WebAPI StreamContent 与 PushStreamContent的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆