重命名后DotNetZip腐败

时间:2013-03-24 16:48:39

标签: c# compression dotnetzip

我正在使用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)here

这是我用来验证它遇到此问题的代码:

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");
}

2 个答案:

答案 0 :(得分:2)

我无法使用随机的ZIP文件重现您的问题我最近的稳定版1.9.1.8减少了预编译二进制文件或最新的简化源代码。您发布的代码会产生一个ZIP存档我可以使用Windows资源管理器和7zip(我没有WinRAR)成功打开和提取文件。

请注意,Anders Gustafsson's approachyour approach都使用相同的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中的一个错误。

当库从ZIP文件中读取文件条目时,它将完全按照ZIP文件的指定获取文件名,并将其存储在Filename的{​​{1}}属性中,然后将此文件名存储为ZipEntry字典中的键。在您的情况下,文件条目的_entries属性具有以下值:

C:\Users\rotem\Documents\3dsmax\scenes\test.max

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

要删除当前条目,它会规范化当前文件名,并从_entries中删除具有该文件名的条目。因此,它正在尝试删除具有此规范化文件名的条目作为键:

Users/rotem/Documents/3dsmax/scenes/test.max

当然,_entries中没有带有该文件名的密钥。它要删除的条目有一个_entries前缀和所有反斜杠。因此,旧条目不会被删除。

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

Users/rotem/Documents/3dsmax/scenes/newname.max

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

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


已经在DotNetZip CodePlex页面上创建了

Issue #16130

答案 1 :(得分:0)

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

但是如果您查看 DotNetZip CodePlex网站上的C# Examples更新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]似乎使不区分大小写的文件名匹配