更新 ZipArchive 中的文件

问题描述 投票:0回答: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()
调用上引发“内存流不可扩展”异常。

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

c# zip c#-ziparchive
1个回答
4
投票

显然,

ZipArchive
想要扩展或调整ZIP存档流的大小。但是,您提供了具有
fixed
流长度的 MemoryStream(由于使用构造函数
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
using
块中。执行此行时,ZipArchive实例可能尚未将所有数据写入MemoryStream,可能会将某些数据保留在某些内部缓冲区中和/或可能推迟写入某些 ZIP 数据结构。 为了确保

ZipArchive

实际上已将所有必要的数据完全写入 MemoryStream,此处只需将 return packageStream.ToArray(); 移至

ZipArchive
using 块之后即可。在其
using
块的末尾,
ZipArchive
将被释放,这也将确保 ZipArchive 已将所有迄今为止尚未写入的数据写入流中。因此,在 ZipArchive 被处理后访问 MemoryStream 将产生完全更新的 ZIP 存档的完整数据。

旁注:仅对较小的 ZIP 文件执行此操作。
MemoryStream

显然将使用内部数据缓冲区(数组)来保存 MemoryStream 中的数据。然而,packageStream.ToArray();会在

MemoryStream
中创建数据的副本,因此在一段时间内,该例程的内存需求将略高于ZIP存档大小的两倍。

© www.soinside.com 2019 - 2024. All rights reserved.