我正在使用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");
}
答案 0 :(得分:2)
我无法使用随机的ZIP文件重现您的问题我最近的稳定版1.9.1.8减少了预编译二进制文件或最新的简化源代码。您发布的代码会产生一个ZIP存档我可以使用Windows资源管理器和7zip(我没有WinRAR)成功打开和提取文件。
请注意,Anders Gustafsson's approach和your 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
中的键的文件名。
答案 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]
似乎使不区分大小写的文件名匹配