我现在有下面的代码,它使用SAS URI从blob下载zip文件,解压缩并将内容上传到新的容器
var response = await new BlobClient(new Uri(sasUri)).DownloadAsync();
using (ZipArchive archive = new ZipArchive(response.Value.Content))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
BlobClient blobClient = _blobServiceClient.GetBlobContainerClient(containerName).GetBlobClient(entry.FullName);
using (var fileStream = entry.Open())
{
await blobClient.UploadAsync(fileStream, true);
}
}
}
我的代码失败了;流太长";异常:System.IO.IOException:流太长。在System.IO.Compression.ZipArchive.Init(流流,ZipArchiveMode模式,布尔leaveOpen(的System.IO.Stream.CopyTo(流目的地,Int32缓冲区大小(处的System.IO.MemoryStream.Write(Byte[]缓冲区,Int32偏移量,Int32计数(。
我的zip文件大小是9G。有什么更好的方法可以绕过这个例外?我希望避免将任何文件写入磁盘。
所以这里的问题是
- .Net的数组大小有限(取决于平台(
- 数组返回流作为缓冲区或内存中的数据存储
- 在64位平台上,阵列大小为2GB
- 您想要在大型对象堆上放置一个9gig流(由数组支持(
因此,您需要允许更大的对象(以某种方式(
允许大型对象
- 在.Net Framework 4.5+中,您可以设置
<gcAllowVeryLargeObjects>
项目元素 - 在核心中,您需要设置环境变量
COMPlus_gcAllowVeryLargeObjects
然而,在大型对象堆上放置9个gig的任何东西都是有问题的,这对GC和其他问题来说效率低下,而且你应该尽可能地避免LOH。
注意,这取决于库和您可以访问的内容。可能有更少的LOHy方法来做到这一点。如果你可以提供自己的流/数据结构,那么有一些库可以分解缓冲区,这样它们就不会通过ReadOnlySequence
和微软鲜为人知的RecyclableMemoryStream
之类的东西在LOH上被积极分配。
下面的解决方案对我有效。不要使用DownloadAsync,而是使用OpenReadAsync
var response = await new BlobClient(new Uri(sasUri)).OpenReadAsync(new BlobOpenReadOptions(false), cancellationToken);
using (ZipArchive archive = new ZipArchive(response))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
BlobClient blobClient = _blobServiceClient.GetBlobContainerClient(containerName).GetBlobClient($"{buildVersion}/{entry.FullName}");
using (var fileStream = entry.Open())
{
await blobClient.UploadAsync(fileStream, true, cancellationToken).ConfigureAwait(false);
}
}
}