C# 正在使用字节数组创建对象列表:OutOfMemoryException
我有一个.NETCore1.1应用程序,它在生成包含字节数组的对象列表时遇到问题。如果列表中有超过20项(任意,我不确定失败的确切数目或大小),该方法将抛出OutOfMemoryException。方法如下:C# 正在使用字节数组创建对象列表:OutOfMemoryException,c#,azure,.net-core,azure-storage-blobs,C#,Azure,.net Core,Azure Storage Blobs,我有一个.NETCore1.1应用程序,它在生成包含字节数组的对象列表时遇到问题。如果列表中有超过20项(任意,我不确定失败的确切数目或大小),该方法将抛出OutOfMemoryException。方法如下: public async Task<List<Blob>> GetBlobsAsync(string container) { List<Blob> retVal = new List<Blob>();
public async Task<List<Blob>> GetBlobsAsync(string container)
{
List<Blob> retVal = new List<Blob>();
Blob itrBlob;
BlobContinuationToken continuationToken = null;
BlobResultSegment resultSegment = null;
CloudBlobContainer cont = _cbc.GetContainerReference(container);
resultSegment = await cont.ListBlobsSegmentedAsync(String.Empty, true, BlobListingDetails.Metadata, null, continuationToken, null, null);
do
{
foreach (var bItem in resultSegment.Results)
{
var iBlob = bItem as CloudBlockBlob;
itrBlob = new Blob()
{
Contents = new byte[iBlob.Properties.Length],
Name = iBlob.Name,
ContentType = iBlob.Properties.ContentType
};
await iBlob.DownloadToByteArrayAsync(itrBlob.Contents, 0);
retVal.Add(itrBlob);
}
continuationToken = resultSegment.ContinuationToken;
} while (continuationToken != null);
return retVal;
}
public异步任务GetBlobsAsync(字符串容器)
{
List retVal=新列表();
Blob-itrBlob;
BlobContinuationToken continuationToken=null;
BlobResultSegment resultSegment=null;
CloudBlobContainer cont=_cbc.GetContainerReference(容器);
resultSegment=await cont.ListBlobsSegmentedAsync(String.Empty,true,BlobListingDetails.Metadata,null,continuationToken,null,null);
做
{
foreach(resultSegment.Results中的变量bItem)
{
var iBlob=bItem作为CloudBlockBlob;
itrBlob=新Blob()
{
Contents=新字节[iBlob.Properties.Length],
Name=iBlob.Name,
ContentType=iBlob.Properties.ContentType
};
等待iBlob.downloadtobytearayasync(itrBlob.Contents,0);
retVal.Add(itrBlob);
}
continuationToken=resultSegment.continuationToken;
}while(continuationToken!=null);
返回返回;
}
我没有使用任何可以在方法中处理的东西。有没有更好的方法来实现这一点?最终目标是提取所有这些文件,然后创建一个ZIP存档。只要我不超过某个大小阈值,这个过程就可以工作
如果有帮助,应用程序将从Azure Web应用程序实例访问Azure Block Blob存储。也许有一个设置我需要调整以增加阈值
实例化Blob()对象时引发异常
编辑:
因此,发布的问题在细节方面无疑是薄弱的。问题容器有30个文件(大部分是压缩良好的大型文本文件)。容器的总大小为971MB。在报告HTTP 500错误和引用的异常之前,请求运行约40秒 当我在本地调试并单步执行相同的操作时,它会成功,生成237MB的zip文件。在操作过程中,我可以看到创建列表时内存使用量超过2GB
我试图将blob存储的交互抽象到它自己的服务中,但可能是我自己让这变得更加困难。发现这两个代码示例非常好地说明了支持您的用例的概念
zipOutputStream.SetLevel(3); //0-9, 9 being the highest level of compression
- 可以在这个结构良好的应用程序中添加Zip功能
public async Task<Stream> GetBlobsAsZipAsync(string container)
{
BlobContinuationToken continuationToken = null;
BlobResultSegment resultSegment = null;
byte[] buffer = new byte[4194304];
MemoryStream ms = new MemoryStream();
CloudBlobContainer cont = _cbc.GetContainerReference(container);
resultSegment = await cont.ListBlobsSegmentedAsync(String.Empty, true, BlobListingDetails.Metadata, null, continuationToken, null, null);
using (var za = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
do
{
foreach (var bItem in resultSegment.Results)
{
var iBlob = bItem as CloudBlockBlob;
var ze = za.CreateEntry(iBlob.Name);
using (var fs = await iBlob.OpenReadAsync())
{
using (var dest = ze.Open())
{
int count = await fs.ReadAsync(buffer, 0, buffer.Length);
while (count > 0)
{
await dest.WriteAsync(buffer, 0, count);
count = await fs.ReadAsync(buffer, 0, buffer.Length);
}
}
}
}
continuationToken = resultSegment.ContinuationToken;
} while (continuationToken != null);
}
return ms;
}
我希望这对其他需要朝正确方向努力的人有用。最初我懒散地处理了这个问题,结果它又咬了我一口。你是在32位模式下运行的吗?然后突破32位的限制?无论如何,如果这些文件如此之大,为什么不将它们保存到一些持久性存储中,并传递链接,而不是将整个文件保存在内存中?问题容器中有30个文件,总容量为971MB。在大多数情况下,这些文件由链接引用,但此函数用于将所有相关文件作为ZIP文件下载。大多数文件集都小于50MB,这一个文件集只比其他文件集大很多。应用程序以64位模式运行。尽管如此,我仍然在2GB以下。正如Michael所说,问题很可能是你在Azure上以32位模式运行;这是默认的。正如您所说,当您在本地运行它时,您可以看到内存超过2gb,这并不奇怪,因为您正在将blob下载到内存并将其复制到字节数组。。。64位只是一种变通方法。简而言之,您应该将每个Bob作为流下载,并立即将其写入zip编写器,然后将其处理掉。您还可以让zip编写器写入临时文件,而不是内存流。说到IO和内存压力,抽象不是你的朋友:)你还需要帮助吗?根据您的回答,我可以提供代码示例,以使这项工作具有较低的内存占用。此外,在web应用程序中以这种方式创建Zip文件通常会导致HTTP请求超时,而且扩展性非常差。希望您能从给定的示例中获益,我自己还没有时间编译工作示例代码,如果您在应用此应用程序时遇到问题,可能稍后再进行。
[HttpPost]
public async Task<IActionResult> DownloadFiles(string container, int projectId, int? profileId)
{
MemoryStream ms = null;
_ctx.Add(new ProjectDownload() { ProfileId = profileId, ProjectId = projectId });
await _ctx.SaveChangesAsync();
using (ms = (MemoryStream)await _blobs.GetBlobsAsZipAsync(container))
{
return File(ms.ToArray(), "application/zip", "download.zip");
}
}