的WebAPI StreamContent VS PushStreamContent [英] WebAPI StreamContent vs PushStreamContent

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

问题描述

我实施BluImp jQuery的文件上传的 MVC4 +的WebAPI版本所有与我最初的尝试效果很好,但我试着去保证内存的最佳使用,同时在下载非常大的文件(〜2GB)。

我读过菲利普Woj对PushStreamContent文章实施它作为最好的,我可以(删除异步部分 - 也许这是什么问题?)。当进出口运行测试,看任务管理器林没有看到太大的区别内存使用明智和我试着去理解之间的反应是如何被处理的差异。

下面是我的StreamContent版本:

 私人的Htt presponseMessage DownloadContentNonChunked()
{
    变种文件名= HttpContext.Current.Request [F];
    VAR文件路径= _storageRoot +文件名;
    如果(File.Exists(文件路径))
    {
        HTT presponseMessage响应=新HTT presponseMessage(的HTTPStatus code.OK);
        response.Content =新StreamContent(新的FileStream(文件路径,FileMode.Open,FileAccess.Read));
        response.Content.Headers.ContentType =新MediaTypeHeaderValue(应用程序/八位字节流);
        response.Content.Headers.ContentDisposition =新ContentDispositionHeaderValue(附件)
        {
            文件名=文件名
        };
        返回响应;
    }
    返回ControllerContext.Request.CreateErrorResponse(的HTTPStatus code.NotFound,);
}

这是我的PushStreamContent版本:

 公共类FileDownloadStream
{
    私人只读字符串_filename;    公共FileDownloadStream(字符串文件路径)
    {
        _filename =文件路径;
    }    公共无效WriteToStream(流的OutputStream,HttpContent内容,TransportContext上下文)
    {
        尝试
        {
            VAR缓冲=新的字节[4096];            使用(VAR视频= File.Open(_filename,FileMode.Open,FileAccess.Read))
            {
                VAR长度=(INT)video.Length;
                变种读取动作= 1;                而(长度大于0&放大器;&放大器;读取动作大于0)
                {
                    读取动作= video.Read(缓冲液,0,Math.Min(长度,buffer.Length));
                    outputStream.Write(缓冲液,0,读取动作);
                    长度 - =读取动作;
                }
            }
        }
        赶上(HttpException前)
        {
            返回;
        }
        最后
        {
            outputStream.Close();
        }
    }
}私人的Htt presponseMessage DownloadContentChunked()
{
    变种文件名= HttpContext.Current.Request [F];
    VAR文件路径= _storageRoot +文件名;
    如果(File.Exists(文件路径))
    {
        VAR fileDownload =新FileDownloadStream(文件路径);
        变种响应= Request.CreateResponse();
        response.Content =新PushStreamContent(fileDownload.WriteToStream,新MediaTypeHeaderValue(应用程序/八位字节流));
        response.Content.Headers.ContentDisposition =新ContentDispositionHeaderValue(附件)
        {
            文件名=文件名
        };
        返回响应;
    }
    返回ControllerContext.Request.CreateErrorResponse(的HTTPStatus code.NotFound,);
}

我的问题是,为什么我没有看到这两种方法之间的内存使用多大差别?此外伊夫下载PDB为StreamContent类型,并且可以看到引用缓冲区的大小和来回等(见下文),所以我想知道到底是什么PushStreamContent被超越StreamContent做。香港专业教育学院检查MSDN上的类型信息,但文章是关于解释一点光!

 命名空间System.Net.Http
{
  ///<总结>
  ///提供基于流的HTTP内容。
  ///< /总结>
  [__DynamicallyInvokable]
  公共类StreamContent:HttpContent
  {
    专用流的内容;
    私人诠释缓冲区大小;
    私人布尔contentConsumed;
    私人长期的开始;
    私人const int的defaultBufferSize = 4096;    ///<总结>
    ///创建℃的新实例;见CREF =T:System.Net.Http.StreamContent/>类。
    ///< /总结>
    ///< PARAM NAME =内容>作为初始化内容的<见CREF =T:System.Net.Http.StreamContent/>< /参数>
    [__DynamicallyInvokable]
    [TargetedPatchingOptOut(性能关键的内联这种类型的跨NGEN图像边界法)]
    公共StreamContent(流内容)
      :这个(内容,4096)
    {
    }


解决方案

对于这些双方的内存使用量的办法,为StreamContent和PushStreamContent,网络API不缓冲的响应。 code的以下快照是从WebHostBufferPolicySelector。来源$ C ​​$ C <一个href=\"http://aspnetwebstack.$c$cplex.com/SourceControl/changeset/view/9e18ded46ea76dac253bb893a66a3475d0cf685c#src/System.Web.Http.WebHost/WebHostBufferPolicySelector.cs\">here.

  ///&LT;总结&gt;
    ///确定主机是否应该缓冲的&lt;见CREF =Htt的presponseMessage/&GT;实体主体。
    ///&LT; /总结&gt;
    ///&LT; PARAM NAME =响应&GT;的&lt;见CREF =Htt的presponseMessage/&GT;反应要为其确定
    ///主机输出缓冲是否应该用于响应实体身体下; /参数&GT;
    ///&LT;&回报GT;&LT; C&GT;真&LT; / c取代;如果缓冲应该使用;否则,应使用一个流式传输的应答下; /回报&GT;
    公共虚拟BOOL UseBufferedOutputStream(HTT presponseMessage响应)
    {
        如果(响应== NULL)
        {
            扔Error.ArgumentNull(回应);
        }        //任何HttpContent,知道它的长度已经是内部缓冲presumably。
        HttpContent内容= response.Content;
        如果(内容!= NULL)
        {
            长? CONTENTLENGTH = content.Headers.ContentLength;
            如果(contentLength.HasValue&放大器;&放大器; contentLength.Value&GT; = 0)
            {
                返回false;
            }            //内容长度为空或-1(意思是不知道)。
            //缓冲区中的任何HttpContent除了StreamContent和PushStreamContent
            返回(内容是StreamContent ||内容PushStreamContent)!;
        }        返回false;
    }

此外PushStreamContent是,你需要推的数据流中的场景,在从流StreamContent'拉'的数据。因此,对于当前的下载文件的情况下,使用StreamContent应该罚款。

下面的例子:

  //这里当响应正被写入了数据从文件到目的地拉(网络)流
response.Content =新StreamContent(File.OpenRead(文件路径));//这里我们创建一个推流的内容,这样我们就可以使用XDocument.Save将数据推到目的地(网络)流
XDOC的XDocument = XDocument.Load(sample.xml的LoadOptions.None);
PushStreamContent xDocContent =新PushStreamContent(
(流,内容,背景)=&GT;
{
     //保存后,我们关闭流信号,我们写完。
     xDoc.Save(流);
     stream.Close();
},
应用程序/ XML);

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).

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.

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

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

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)
    {
    }

解决方案

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;
    }

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.

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

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

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