1

我正在使用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");
}
4

2 回答 2

2

我无法使用我放置的随机 ZIP 文件和最新的稳定 1.9.1.8 Reduced 预编译二进制文件或最新的 Reduced 源代码重现您的问题。您在 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 中的一个错误。

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

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

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

要删除当前条目,它会规范化当前文件名并从_entries. 因此,它试图以这个规范化的文件名作为键来删除一个条目:

用户/rotem/Documents/3dsmax/scenes/test.max

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

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

用户/rotem/Documents/3dsmax/scenes/newname.max

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

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


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

于 2013-03-27T18:44:45.007 回答
0

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

但是,如果您查看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();
    }
}

据我从测试中可以看出,上述方法按预期工作,并且 indexer 属性this[string fileName]似乎使不区分大小写的文件名匹配

于 2013-03-27T13:00:30.267 回答