重命名后DotNetZip损坏

本文关键字:损坏 DotNetZip 重命名 | 更新日期: 2023-09-27 18:05:38

我正在使用DotNetZip库来重命名zip存档中的文件。

下面是我使用的代码:
void RenameFile(string existingArchive, string oldName, string newName)
{
    using (ZipFile zip = ZipFile.Read(existingArchive))
    {
        ZipEntry targetFile = zip.Entries.FirstOrDefault(f =>
            f.FileName.ToLower() == oldName.ToLower());
        //targetFile is not null.
        targetFile.FileName = newName;
        zip.Save();
    }
}

我遇到的问题是,在重命名操作后,zip已损坏。当我使用WinRAR浏览它时,我重命名的文件有一个重复的条目(两个条目都是相同的,只是使用了新名称)。当我试图提取它时,WinRAR对两个相同的条目抛出CRC错误。

我试过保存到一个不同的文件名,但是没有运气。

我使用的是v1.9.1.8的库(精简版)。

更新:

在重命名后删除其中一个重复条目似乎有效并且没有明显的副作用。不过,一个不那么俗套的方法是受欢迎的。

void RenameFile(string existingArchive, string oldName, string newName)
{
    using (ZipFile zip = ZipFile.Read(existingArchive))
    {
        ZipEntry targetFile = zip.Entries.FirstOrDefault(f =>
            f.FileName.ToLower() == oldName.ToLower());
        //targetFile is not null.
        targetFile.FileName = newName;
        //if the file was duplicated, remove one of the duplicates:
        var dupFiles = zip.Where(f => f.FileName == newName).ToList();
        if (dupFiles.Count > 1)
        {
            zip.RemoveEntries(dupFiles.Skip(1).ToList());
        }
        zip.Save();
    }
}
更新2:

由于答案表明源文件可能是问题所在,我在这里上传了一个示例zip文件(24kb)

这是我用来验证它是否存在这个问题的代码:

using (ZipFile zip = ZipFile.Read("test.zip"))
{
    ZipEntry entry = zip.Entries.FirstOrDefault(e => Path.GetFileName(e.FileName) == "test.max");
    entry.FileName = Path.Combine(Path.GetDirectoryName(entry.FileName), "newname.max");
    zip.Save(@"test2.zip");
}

重命名后DotNetZip损坏

我无法用我周围的随机ZIP文件和最新稳定的1.9.1.8减少预编译二进制文件或最新减少的源代码重现您的问题。你发布的代码结果在ZIP存档,我可以成功地打开和提取文件从使用Windows资源管理器和7zip(我没有WinRAR)。

请注意,Anders Gustafsson的方法和你的方法都使用相同的ZipFile的内部数据结构,所以应该没有区别,你的代码应该可以正常工作。

public ZipEntry this[String fileName]
{
    get
    {
        if (_entries.ContainsKey(fileName))
            return _entries[fileName];
        return null;
    }
}
public ICollection<ZipEntry> Entries
{
    get { return _entries.Values; }
}

可能是您正在使用的源ZIP文件的问题,或者是该ZIP文件的完整路径(太长?),或者是您选择的新旧文件名的问题?或者您打开了另一个应用程序,以某种方式阻止DotNetZip删除旧条目?


更新:发现问题!

这个问题确实是你的ZIP文件独有的,但实际上它是DotNetZip的一个bug。

库从ZIP文件中读取文件条目时,取ZIP文件指定的文件名,并将其存储在ZipEntryFilename属性中,然后将该文件名作为键存储在_entries字典中。在您的示例中,文件条目的Filename属性具有以下值:

<>之前C:'Users'rotem'Documents'3dsmax'scenes'test.max之前

然后,当您更改Filename属性的值时,DotNetZip将从_entries中删除当前条目,并使用新的文件名重新添加条目。

要删除当前条目,它将当前文件名规范化,并从_entries中删除具有该文件名的条目。所以,它试图删除一个条目,这个规范化的文件名作为键:

<>之前用户/rotem/文档/3 dsmax软件/场景/test.max之前

当然,_entries中没有这个文件名的键。它要删除的条目有一个C:'前缀和所有反斜杠。因此,旧条目不会被删除。

但是DotNetZip没有注意到这一点并继续。它将条目的Filename设置为规范化的新文件名:

<>之前用户/rotem/文档/3 dsmax软件/场景/newname.max之前

然后将条目重新添加到_entries字典中。现在,同一个文件条目有两个条目:一个具有规范化的新文件名,另一个具有未规范化的旧文件名。这就是你所有问题的来源。

我建议DotNetZip断言它在重命名时确实删除了一个条目。并且它应该规范化任何分配给ZipEntry.Filename属性或其支持字段的文件名,或者在_entries中用作键的文件名。


Issue #16130已在DotNetZip CodePlex页面上创建。

您的方法似乎是合理的,因此很可能在DotNetZip库中存在错误。

BUT如果你看一下DotNetZip CodePlex站点上的c#示例,更新zip存档一节,你会看到推荐的重命名存档中现有条目的方法是:

zip[oldName].FileName = newName;

这意味着你的方法会变成这样:

void RenameFile(string existingArchive, string oldName, string newName)
{
    using (ZipFile zip = ZipFile.Read(existingArchive))
    {
        zip[oldName].FileName = newName;
        zip.Save();
    }
}

从我的测试中可以看出,上面的方法按预期工作,索引器属性this[string fileName]似乎使一个不区分大小写的文件名匹配