【问题标题】:System.OutOfMemoryException when reading content of a file in a Web API在 Web API 中读取文件内容时出现 System.OutOfMemoryException
【发布时间】:2019-02-13 15:55:18
【问题描述】:

我想通过 Amazon Firehose 将文件内容作为 memory stream 发送到 S3 存储桶。下面是我的尝试,它适用于小文件,但我有一个 1 GB 的文件,我得到了{"Exception of type 'System.OutOfMemoryException' was thrown."}

我的代码sn-p:

[HttpPost]
public async Task<bool> Upload()
{
    try
    {
        var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();
        foreach (var stream in filesReadToProvider.Contents)
        {
            var fileBytes = await stream.ReadAsByteArrayAsync(); // THIS IS WHERE EXCEPTION COMES
            using (MemoryStream memoryStream = new MemoryStream(fileBytes))
            {
                PutRecordRequest putRecord = new PutRecordRequest();
                putRecord.DeliveryStreamName = myStreamName;
                Record record = new Record();
                record.Data = memoryStream;
                putRecord.Record = record;
                await kinesisClient.PutRecordAsync(putRecord);
            }
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
        throw;
    }    
    return true;
}

我查看了这个链接OutOfMemoryExceptoin,但我无法理解。请帮帮我。

尝试 1:

var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();
foreach (var stream in filesReadToProvider.Contents)
{
    var fileByte = await stream.ReadAsStreamAsync();
    MemoryStream _ms = new MemoryStream();
    fileByte.CopyTo(_ms); // EXCEPTION HERE
    try
    {
        PutRecordRequest putRecord = new PutRecordRequest();
        putRecord.DeliveryStreamName = myStreamName;
        Record record = new Record();
        record.Data = _ms;
        putRecord.Record = record;
        await kinesisClient.PutRecordAsync(putRecord);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Failed to send record to Kinesis. Exception: {0}", ex.Message);
    }
}

【问题讨论】:

  • 为什么要将整个流读入一个数组?如果要流式传输字节,请使用 ReadAsStreamAsync
  • 尝试使用GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); 或者如果文件很大,将其分成块。
  • 您应用于文件内容的逻辑在这种情况下是相关的。您应该尽量避免读取所有文件然后处理它,而是分块读取和处理。

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


【解决方案1】:
[HttpPost]
public async Task<bool> Upload()
{
    try
    {
        using(var requestStream = await Request.Content.ReadAsStreamAsync())
        {
            PutRecordRequest putRecord = new PutRecordRequest();
            putRecord.DeliveryStreamName = myStreamName;
            Record record = new Record();
            record.Data = requestStream ;
            putRecord.Record = record;
            await kinesisClient.PutRecordAsync(putRecord);

        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
        throw;
    }

    return true;
}

这将读取块中的数据。将所有内容都保存在 Stream 中,这样您就不会将所有字节都保存在一个巨大的数组中。

【讨论】:

  • 非常感谢,但它不起作用,Record class 需要 Memory Stream。请看这个链接docs.aws.amazon.com/sdkfornet/latest/apidocs/items/…
  • 在我的问题中查看我的Attempt 1 。当我尝试将流转换为内存流时,我再次遇到了同样的异常
  • 您能告诉我如何分块阅读它。我认为这是我唯一的选择。
  • 看起来 Amazon Kinesis 记录不应该大于 1 MB
【解决方案2】:

在读取大文件时,我使用 StreamReader 的 Readline() 方法。它适用于大文件,因为它在内部管理文件系统缓存。你可以改用这个方法吗?您实现 MemoryStream 类是否有原因?您有评论询问如何注入数据?您是否尝试过使用 MemoryStream 的方法之一???

https://docs.microsoft.com/en-us/dotnet/api/system.io.memorystream?view=netframework-4.7.2

更新:

不确定这是否有用,因为代码与您使用的代码大不相同。但是,你的也不起作用,所以只是一个建议。

http://www.tugberkugurlu.com/archive/efficiently-streaming-large-http-responses-with-httpclient

【讨论】:

  • 主题开始并没有提及数据性质,如果是二进制数据,您使用 RedLine 的建议可能会在处理过程中引入更多复杂性。
  • @lerthe61 OP 刚刚更新了问题以解释他们为什么使用 MemoryStream。此外,inject 评论已被删除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-18
  • 1970-01-01
  • 2019-07-07
  • 2013-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多