来自第三方的C#Stream响应,最小缓冲

时间:2016-05-31 04:22:09

标签: c# asp.net-mvc

我们的ASP.NET MVC端点是另一个第三方HTTP端点的代理,它返回大约400MB的动态生成的XML文档。

是否有一种方法可以让ASP.NET MVC流动"流"第三方直接向我们的终端用户做出响应" minimal"缓冲?

目前,ASP.NET System.Web.Mvc.Controller.File()似乎将整个文件作为响应加载到内存中。 除了内存使用量的跳跃之外,我不确定如何确认这一点?

System.Web.Mvc.Controller.File(

IIS AppPool内存使用量增加400MB,然后由垃圾收集重新声明。

如果我们可以避免System.Web.Mvc.Controller.File()将整个400MB字符串加载到内存中,通过流式传输几乎直接"这将是很好的。来自回复, 有可能吗?

模拟c#linqpad代码大致与此类似

public class MyResponseItem {
    public Stream myStream;
    public string metadata;
}

void Main()
{
    Stream stream = MyEndPoint();   

    //now let user download this XML as System.Web.Mvc.FileResult
    System.Web.Mvc.ActionResult fileResult = System.Web.Mvc.Controller.File(stream, "text/xml");
    fileResult.Dump();
}

Stream MyEndPoint() {
    MyResponseItem myResponse = GetStreamFromThirdParty("https://www.google.com");
    return myResponse.myStream;
}

MyResponseItem GetStreamFromThirdParty(string fullUrl)
{   
    MyResponseItem myResponse = new MyResponseItem();   
    System.Net.WebResponse webResponse = System.Net.WebRequest.Create(fullUrl).GetResponse();
    myResponse.myStream = webResponse.GetResponseStream();
    return myResponse;
}

2 个答案:

答案 0 :(得分:5)

您可以通过不缓冲来减少内存占用,只需将流直接复制到输出流,快速n'这里有一个肮脏的例子:

    public async Task<ActionResult> Download()
    {
        using (var httpClient = new System.Net.Http.HttpClient())
        {
            using (
                var stream = await httpClient.GetStreamAsync(
                    "https://ckannet-storage.commondatastorage.googleapis.com/2012-10-22T184507/aft4.tsv.gz"
                    ))
            {
                Response.ContentType = "application/octet-stream";
                Response.Buffer = false;
                Response.BufferOutput = false;
                await stream.CopyToAsync(Response.OutputStream);
            }
            return new HttpStatusCodeResult(200);
        }
    }

如果您想减少占用空间,可以设置较低的缓冲区大小CopyToAsync(Stream, Int32)重载,默认为81920字节。

答案 1 :(得分:0)

我对代理下载的要求还需要确保也可以转发源ContentType(或您需要的任何标头)。 (例如,如果我在http://techslides.com/sample-webm-ogg-and-mp4-video-files-for-html5中代理下载视频,则需要让用户看到相同的浏览器-视频播放器屏幕,因为他们直接打开链接,但不能跳转到文件下载/硬编码的ContentType)

基于@devlead +另一则帖子https://stackoverflow.com/a/30164356/4684232的回答,我对回答的范围进行了调整以满足自己的需要。如果有人有同样的需求,这是我经过调整的代码。

public async Task<ActionResult> Download(string url)
{
    using (var httpClient = new System.Net.Http.HttpClient())
    {
        using (var response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
        {
            response.EnsureSuccessStatusCode();
            using (var stream = await response.Content.ReadAsStreamAsync())
            {
                Response.ContentType = response.Content.Headers.ContentType.ToString();
                Response.Buffer = false;
                Response.BufferOutput = false;
                await stream.CopyToAsync(Response.OutputStream);
            }
        }

        return new HttpStatusCodeResult(200);
    }
}

p.s。 HttpCompletionOption.ResponseHeadersRead是重要的性能关键。如果没有它,GetAsync将一直等到整个源响应流都被下载为止,这要慢得多。