首页 文章

更新ZipArchive中的文件

提问于
浏览
1

我有一个ZipArchive对象,其中包含我正在修改的XML文件 . 然后我想返回修改后的ZipArchive .

这是我的代码:

var package = File.ReadAllBytes(/* location of existing .zip */);

using (var packageStream = new MemoryStream(package, true))
using (var zipPackage = new ZipArchive(packageStream, ZipArchiveMode.Update))
{
    // obtain the specific entry    
    var myEntry = zipPackage.Entries.FirstOrDefault(entry => /* code elided */));

    XElement xContents;
    using (var reader = new StreamReader(myEntry.Open()))
    {
        // read the contents of the myEntry XML file
        // then modify the contents into xContents
    }

    using (var writer = new StreamWriter(myEntry.Open()))
    {
        writer.Write(xContents.ToString());
    }

    return packageStream.ToArray();
}

此代码在 packageStream.ToArray() 调用上抛出"Memory stream is not expandable"异常 .

任何人都可以解释我做错了什么,以及更新ZipArchive中现有文件的正确方法是什么?

1 回答

  • 3

    显然, ZipArchive 想要扩展或调整ZIP存档流的大小 . 但是,您提供了一个 MemoryStream ,其流长度为 fixed (由于使用了构造函数 MemoryStream(byte[], bool) ,它创建的内存流的固定长度等于提供给构造函数的数组的长度) .

    由于 ZipArchive 想要扩展(或调整大小)流,因此请提供可调整大小的 MemoryStream (使用其无参数构造函数) . 然后将原始文件数据复制到此MemoryStream中并继续ZIP存档操作 .

    并且不要忘记在将原始文件数据复制到其中后将MemoryStream读/写位置重置回 0 ,否则ZipArchive在尝试从此流中读取ZIP存档数据时将只看到“End of Stream” .

    using (var packageStream = new MemoryStream())
    {
        using (var fs = File.OpenRead(/* location of existing .zip */))
        {
            fs.CopyTo(packageStream);
        }
    
        packageStream.Position = 0;
    
    
        using (var zipPackage = new ZipArchive(packageStream, ZipArchiveMode.Update))
        {
            ... do your thing ...
        }
    
    
        return packageStream.ToArray();
    }
    

    此代码包含一个更正 . 在问题的原始代码中, return packageStream.ToArray(); 已被放置在ZipArchive的 usingusing 块中 . 在执行此行时,ZipArchive实例可能尚未将所有数据写入MemoryStream,可能仍将某些数据保留在某些内部缓冲区中和/或可能延迟编写某些ZIP数据结构 .

    为了确保ZipArchive实际上已经将所有必要的数据完全写入MemoryStream,在ZipArchive using 块之后移动 return packageStream.ToArray(); 就足够了 . 在 using 块的末尾,ZipArchive将被释放,这也将确保ZipArchive已将所有到目前为止尚未写入的数据写入流中 . 因此,在ZipArchive处理完毕后访问MemoryStream将产生完全更新的ZIP存档的完整数据 .


    附注:仅使用小型ZIP文件执行此操作 . MemoryStream显然会使用内部数据缓冲区(数组)来保存MemoryStream中的数据 . 但是, packageStream.ToArray(); 将在MemoryStream中创建数据的副本,因此在一段时间内,此例程的内存要求将略大于ZIP存档大小的两倍 .

相关问题