繁体   English   中英

来自第 3 方的 C# 流响应,最小缓冲

[英]C# Stream Response from 3rd party, minimal buffering

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

有没有办法让 ASP.NET MVC 以“最小”缓冲将第 3 方响应直接“流式传输”到我们端点的用户?

目前,看起来 ASP.NET System.Web.Mvc.Controller.File() 将整个文件加载到内存中作为响应。 除了内存使用量增加之外,不知道如何确认这一点?

System.Web.Mvc.Controller.File(

IIS AppPool 内存使用量增加了 400MB,之后会被 Garbage Collection 回收。

如果我们可以避免 System.Web.Mvc.Controller.File() 将整个 400MB 字符串加载到内存中,通过从传入响应中“几乎直接”流式传输它,那会很好,这可能吗?

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

您可以通过不缓冲并直接将流复制到输出流来减少内存占用,这里是一个快速的 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 字节。

我对代理下载的要求还需要确保源 ContentType(或您需要的任何 Header)也可以被转发。 (例如,如果我在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);
    }
}

ps HttpCompletionOption.ResponseHeadersRead 是重要的性能关键。 没有它,GetAsync 将等待直到整个源响应流被下载,这会慢得多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM