【问题标题】:How do I stream a large file from api to api without using disk and running out of memory?如何在不使用磁盘和内存不足的情况下将大文件从 api 流式传输到 api?
【发布时间】:2018-10-18 20:23:08
【问题描述】:

我有一个 API HTTPGET 可以从另一个 API 检索文件,然后将其吐出。这适用于较小的文件,但我遇到的问题是检索到的文件可能相当大(最多 2gb),并且 MemoryStream 是一个限制。任何想法如何在不使用磁盘的情况下流式传输文件内容并避免“内存不足”异常?

控制器:

[Route("{id}/file", Name = "GetContentFile")]
[HttpGet]
public IHttpActionResult GetContentFile(string id)
{
    if (String.IsNullOrEmpty(id))
        return BadRequest();
    ContentFile cfl = new ContentFile();
    var ret = new HttpResponseMessage(HttpStatusCode.OK);
    try
    {
        cfl = otcrepo.GetContentFile(id);
        var mstream = new MemoryStream(cfl.Data);
        ret.Content = new StreamContent(mstream);
        ret.Content.Headers.ContentType = new MediaTypeHeaderValue(cfl.ContentType);
        ret.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
    }
    catch
    {
        return InternalServerError();
    }
    if (cfl != null)
    {
        ResponseMessageResult responseMessageResult = ResponseMessage(ret);
        return responseMessageResult;
    }
    else
    {
        return NotFound();
    }
}

型号:

public class ContentFile
{
    public string Filename { get; set; }
    public byte[] Data { get; set; }
    public StreamContent DataStream { get; set; }
    public string ContentType { get; set; }
}

存储库调用:

public ContentFile GetContentFile(string id)
{
    ContentFile fl = new ContentFile();
    using (var htc = new HttpClient())
    {
        var response = htc.GetAsync(ConfigurationManager.AppSettings["BaseUrl"] + "/api/v2/nodes/" + id + "/content/").Result;

        fl.Data = response.Content.ReadAsByteArrayAsync().Result;
        fl.ContentType = response.Content.Headers.GetValues("Content-Type").FirstOrDefault();
    }
    return fl;
}

谢谢。

【问题讨论】:

  • 不使用内存?
  • 如果你不愿意使用内存磁盘,我看不出这是怎么可能的。其他选择是什么(除了内存或磁盘)?
  • 我会改写我的问题,因为正如您所指出的,必须使用一些内存。如何在避免outofmemory异常的同时做到这一点?
  • 哇,等一下,你还有其他问题要担心。 您在异步任务上使用.Result。那是非常危险的!这可能会使您的服务器死锁。出于某种原因,它是异步的;你必须await那个。在尝试快速获取代码之前先获取代码。
  • 除此之外,我不明白你为什么要这样做。您说您不想将文件放入内存中,但是您正在将其作为字节数组读入内存。如果要流式传输它,为什么要将其移动到字节数组中,然后将流放在字节数组的顶部?这是一件非常奇怪的事情。你为什么不直接打电话给GetResponseStream?你想要一个流,好吧,得到一个流。不要获取字节数组并在其上构建流。

标签: c# .net asp.net-web-api asp.net-web-api2


【解决方案1】:

所以不是GetContentFile 将完整的流读入数组并返回,而是返回流。比没有必要MemoryStream

[Route("{id}/file", Name = "GetContentFile")]
[HttpGet]
public async Task<IHttpActionResult> GetContentFile(string id)
{
    ...
    var result = await otcrepo.GetContentFile(id);
    ret.Content = new StreamContent(result.stream);
    ret.Content.Headers.ContentType = new MediaTypeHeaderValue(result.contentType);
    ...

}

public async Task<(Stream stream, string contentType)> GetContentFile(string id)
{
    var htc = new HttpClient()
    var response = await htc.GetAsync(ConfigurationManager.AppSettings["BaseUrl"] + "/api/v2/nodes/" + id + "/content/");

    var stream = await response.Content.ReadAsStreamAsync();
    var contentType = response.Content.Headers.GetValues("Content-Type").FirstOrDefault();
    return (stream, contentType);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-06-29
    • 2011-10-15
    • 1970-01-01
    • 2015-07-13
    • 2013-03-23
    • 2020-07-09
    • 1970-01-01
    • 2022-10-08
    相关资源
    最近更新 更多