You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Microsoft.IO.RecyclableMemoryStream将字节数组生成ZIP文件?

问题:RecyclableMemoryStream与ZipArchive配合的归档异常问题

我需要将字节数组保存为ZIP压缩文件,由于Azure共享服务器实例内存限制,使用Microsoft.IO.RecyclableMemoryStream避免内存不足,但它和ZipArchive配合出现了以下问题:

1. 生成损坏归档的尝试

第一次尝试的代码会生成无法打开的未完成归档:

var data = Encoding.UTF8.GetBytes("This is a test string");

byte[] compressedBytes = default;

var manager = new RecyclableMemoryStreamManager();
using var originalStream = manager.GetStream("Raw", data);
using var compressedStream = manager.GetStream("Compressed");
using var zipArchive = new ZipArchive(compressedStream, ZipArchiveMode.Create, false);

var zipEntry = zipArchive.CreateEntry("test.txt");
using var zipEntryStream = zipEntry.Open();
originalStream.CopyTo(zipEntryStream);

// 以下操作无效,归档仍损坏
zipEntryStream.Flush();
zipEntryStream.Close();

compressedBytes = compressedStream.ToArray();

// 结果:文件无法打开,提示"Unexpected end of archive"
// 注:这里直接返回字节仅用于测试,实际控制器中不会这么做
return File(compressedBytes, "application/zip", "test.zip");

结果是生成的ZIP文件无法打开,提示*"Unexpected end of archive"*。

2. 释放ZipArchive后无法访问流的尝试

为了让ZipArchive完成归档,将其放在独立的using块中,但此时底层的RecyclableMemoryStream被释放,无法获取字节:

using var originalStream = manager.GetStream("Raw", data);
using var compressedStream = manager.GetStream("Compressed");

// 独立using块确保ZipArchive及时释放以完成归档
using (var zipArchive = new ZipArchive(compressedStream, ZipArchiveMode.Create, false))
{
    var zipEntry = zipArchive.CreateEntry("test.txt");
    using var zipEntryStream = zipEntry.Open();
    originalStream.CopyTo(zipEntryStream);
}
// 抛出异常:Cannot access a disposed object.
compressedBytes = compressedStream.ToArray();

此时归档完成,但抛出*"Cannot access a disposed object"*异常,无法获取压缩后的字节数组。

3. 使用传统MemoryStream可正常工作

只有使用传统MemoryStream的方式能正常生成可打开的ZIP文件:

var manager = new RecyclableMemoryStreamManager();
using var originalStream = manager.GetStream("Raw", data);
using var compressedStream = new MemoryStream();

// 独立using块确保ZipArchive完成归档
using (var zipArchive = new ZipArchive(compressedStream, ZipArchiveMode.Create, false))
{
    var zipEntry = zipArchive.CreateEntry("test.txt");
    using var zipEntryStream = zipEntry.Open();
    originalStream.CopyTo(zipEntryStream);
}

compressedBytes = compressedStream.ToArray();

解决方案:设置ZipArchive的leaveOpen参数为true

问题的核心是ZipArchive的第三个构造参数leaveOpen,将其设为true,就能让ZipArchive在释放时不关闭底层的RecyclableMemoryStream,同时完成归档的收尾工作。

修正后的代码:

var data = Encoding.UTF8.GetBytes("This is a test string");
byte[] compressedBytes = default;

var manager = new RecyclableMemoryStreamManager();
using var originalStream = manager.GetStream("Raw", data);
using var compressedStream = manager.GetStream("Compressed");

// 将leaveOpen设为true,避免ZipArchive释放时关闭底层流
using (var zipArchive = new ZipArchive(compressedStream, ZipArchiveMode.Create, true))
{
    var zipEntry = zipArchive.CreateEntry("test.txt");
    using var zipEntryStream = zipEntry.Open();
    originalStream.CopyTo(zipEntryStream);
}

// 此时compressedStream未被释放,可正常获取字节数组
compressedBytes = compressedStream.ToArray();

return File(compressedBytes, "application/zip", "test.zip");

原理说明

  • leaveOpen: true是关键:它告诉ZipArchive,在自身被释放时不要关闭传入的底层流,这样RecyclableMemoryStream就能继续被使用来读取压缩后的字节。
  • ZipArchive在释放时会自动完成归档的收尾工作(比如写入ZIP目录结构、校验信息),这解决了第一个尝试中归档损坏的问题。

备选方案:使用第三方压缩库

如果对ZipArchive的行为仍有顾虑,可以考虑使用第三方压缩库,比如SharpZipLib或DotNetZip,这些库对自定义流的兼容性更好,且提供更灵活的归档控制选项。


内容的提问来源于stack exchange,提问作者JustAMartin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 00:06:15