如果调用Save两次,则损坏ZIP文件

时间:2015-09-23 07:54:54

标签: c# stream zip dotnetzip

我在我的应用程序中使用DotNetZip 1.9.6,它使用类似于例如文件结构的文件结构。 * .docx:包含XML文件的Zip文件 现在,应用程序的每个模块都可以将这些XML文件存储到我的自定义文件管理中,然后保存"保存"它们被序列化为流,然后通过DotNetZip保存到Zip文件中 要更新条目,请使用ZipFile.UpdateEntry(path, stream)。 这工作正常,我第一次通过调用ZipFile.Save()保存我的文件一切正常。

但是,如果我在同一个实例上第二次(先是UpdateEntry次调用Save),Zip文件已损坏:文件结构和元数据(例如每个未压缩的大小)文件)仍然存在,但所有文件都是压缩大小的0字节。

如果我在保存一切正常后从刚刚保存的文件创建一个新实例,但是不应该避免这种情况,并且重复使用"相同的实例?

以下示例(另请参阅https://dotnetfiddle.net/mHxEIy)可用于重现问题:

using System.IO;
using System.Text;

public class Program
{
    public static void Main()
    {
        var zipFile = new Ionic.Zip.ZipFile();

        var content1 = new MemoryStream(Encoding.Default.GetBytes("Content 1"));
        zipFile.UpdateEntry("test.txt", content1);

        zipFile.Save("test.zip"); // here the Zip file is correct
        //zipFile = new Ionic.Zip.ZipFile("test.zip"); // uncomment and it works too

        var content2 = new MemoryStream(Encoding.Default.GetBytes("Content 2"));
        zipFile.UpdateEntry("test.txt", content2);

        zipFile.Save();  // after that it is corrupt
    }
}

要运行此功能,您需要添加" DotNetZip 1.9.6" NuGet包。

第一次保存后,这就是你得到的:
Correct
并在第二次保存后:
Corrupt

2 个答案:

答案 0 :(得分:2)

这似乎是图书馆中的一个错误,即删除条目。如果只是删除一个条目然后再次保存,它会正确删除该文件。

但是,如果删除一个条目,然后添加另一个具有相同名称的条目 - 如果该条目已存在,则记录UpdateEntry要执行的操作 - 似乎使用旧条目。

您第二次以空文件结束的原因是原来的MemoryStream正在被再次阅读 - 但到目前为止,它位于数据的末尾,所以没有数据可供阅读。如果将位置重置为流的开头(content1.Position = 0;),它将重写原始数据。如果您修改 content1中的数据,则最终会得到无效的压缩数据。

我能立即想到的唯一解决方法是将您自己的地图从文件名保留到MemoryStream,并在您想要更新时替换每个MemoryStream的内容...或者只是加载根据您现有的解决方法,每次都提交文件。

这绝对值得在此处提出一个错误,因为它应该尽我所能。

答案 1 :(得分:0)

正如已经怀疑这是DotNetZip版本1.9.6之前的错误 我想我能够通过THIS更改解决这个问题,该更改刚刚在NuGet上作为版本1.9.7发布。至少对我来说问题不再发生了。

根据我的发现,发生了一些背景:
当您调用Save时,库会设置一个内部标志,该内部标志会记住ZIP文件只是保存,并且在第二次Save调用而不是“重新压缩”ZIP文件中的所有条目时,它会从刚刚复制它们保存的文件 这适用于添加/删除条目,但在其中一个条目更改时中断,然后它“混合”旧条目和新条目并生成不一致的ZIP文件。
如果条目被更改,我的修复程序基本上禁用“从旧文件复制”逻辑。