重命名后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");
}
我无法用我周围的随机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文件指定的文件名,并将其存储在ZipEntry
的Filename
属性中,然后将该文件名作为键存储在_entries
字典中。在您的示例中,文件条目的Filename
属性具有以下值:
然后,当您更改Filename
属性的值时,DotNetZip将从_entries
中删除当前条目,并使用新的文件名重新添加条目。
要删除当前条目,它将当前文件名规范化,并从_entries
中删除具有该文件名的条目。所以,它试图删除一个条目,这个规范化的文件名作为键:
当然,_entries
中没有这个文件名的键。它要删除的条目有一个C:'
前缀和所有反斜杠。因此,旧条目不会被删除。
但是DotNetZip没有注意到这一点并继续。它将条目的Filename
设置为规范化的新文件名:
然后将条目重新添加到_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]
似乎使一个不区分大小写的文件名匹配