ZipArchiveEntry.ExtractToFile() 适用于“winrar.zip”,但不适用于“msbuild.zip”

ZipArchiveEntry.ExtractToFile() works with "winrar.zip" but not "msbuild.zip"

提问人:Pawcio 提问时间:11/17/2023 更新时间:11/17/2023 访问量:17

问:

所以我使用以下方法来提取档案。

public static void extractArchive(string zipFile, string destination) {
    destination = Path.GetFullPath(destination);
    if (!destination.EndsWith(Path.DirectorySeparatorChar.ToString()))
        destination += Path.DirectorySeparatorChar;

    using (var arc = ZipFile.OpenRead(zipFile)) {
        foreach (var e in arc.Entries) {
            var des = Path.GetFullPath(
                Path.Combine(destination + e.FullName));
            if (des.EndsWith(Path.DirectorySeparatorChar.ToString())) {
                if (!Directory.Exists(des)) Directory.CreateDirectory(des);
                continue;
            }
            if (File.Exists(des) || !des.StartsWith(destination)) continue;
            Console.WriteLine($@"extracting  ""{e}"" to ""{des}""");
            e.ExtractToFile(des);
        }
    }
}

当我使用 WinRAR 提取手动打包的存档时,它可以正常工作,但是当使用 MS Build 任务打包存档时,它会引发异常:

<Target Name="AfterBuild">
  <Message Text="Second occurrence" />
  <ZipDirectory Overwrite="true" SourceDirectory="$(TargetDir)\archive\" DestinationFile="$(TargetDir)\VS_MSBS_task.zip" />
</Target>
static void Main(string[] args) {

    var dest = Directory.GetCurrentDirectory();
    dest = Path.Combine(dest, "output");

    zip("Winrar zip", "Winrar.zip",  dest);
    zip("VisualStudio zip", "VS_MSBS_task.zip",  dest); //exception


    Console.WriteLine("Done");
    Console.ReadKey();
}

private static void zip(string name, string zipFile, string dest) {
    Console.WriteLine($"-----------------{name}--------------------------------------------------------");
    if(Directory.Exists(dest))
        Directory.Delete(dest, true);
    Directory.CreateDirectory(dest);
    try {
        extractArchive(zipFile, dest);
    }catch (Exception ex) {
        Console.WriteLine(ex);
    }
    Console.WriteLine($"-----------------FINISHED------------------------------------------------------");
}

有了“VS_MSBS_task.zip”,我得到:

System.IO.DirectoryNotFoundException:“找不到路径”path\to\file\in\subfolder.extension“的一部分。

//ZipFileExtensions.cs:
public static void ExtractToFile(this ZipArchiveEntry source, string destinationFileName, bool overwrite)
{
    //...
    using (Stream destination = File.Open(destinationFileName, mode, FileAccess.Write, FileShare.None))
    {
        //..
    }
    //..
}

在这两种情况下,该方法的输入看起来完全相同,所以我想这是一个错误,或者我遗漏了什么?System.IO.Compression

下面是控制台输出:

-----------------Winrar zip--------------------------------------------------------
extracting  "sub1/fileInSubFolder.cs" to "C:\...\ZipExtractTest\bin\Debug\output\sub1\fileInSubFolder.cs"
extracting  "text.txt" to "C:\...\ZipExtractTest\bin\Debug\output\text.txt"
-----------------FINISHED------------------------------------------------------
-----------------VisualStudio zip--------------------------------------------------------
extracting  "text.txt" to "C:\...\ZipExtractTest\bin\Debug\output\text.txt"
extracting  "sub1/fileInSubFolder.cs" to "C:\...\ZipExtractTest\bin\Debug\output\sub1\fileInSubFolder.cs"
System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\...\ZipExtractTest\bin\Debug\output\sub1\fileInSubFolder.cs'.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
   at System.IO.Compression.ZipFileExtensions.ExtractToFile(ZipArchiveEntry source, String destinationFileName, Boolean overwrite)
   at System.IO.Compression.ZipFileExtensions.ExtractToFile(ZipArchiveEntry source, String destinationFileName)
   at ZipExtractTest.Program.extractArchive(String zipFile, String destination) in C:\...\ZipExtractTest\Program.cs:line 48
   at ZipExtractTest.Program.zip(String name, String zipFile, String dest) in C:\...\ZipExtractTest\Program.cs:line 26
-----------------FINISHED------------------------------------------------------
Done

您可以在此处找到完整代码

ZipArchiveEntry 文档

C# .NET MSBuild 压缩包

评论

0赞 Jonathan Dodds 11/17/2023
似乎您应该收到“已存在”的信号。如果翻转调用的顺序,即先解压缩,则第二次调用时是否继续发生异常?IOExceptiondestinationFileNamezip()"VS_MSBS_task.zip"zip()
0赞 Pawcio 11/20/2023
不,该方法在调用之前会删除整个输出目录,因此调用顺序无关紧要。现在我开始怀疑问题出在zip()extractArchive()if (des.EndsWith(Path.DirectorySeparatorChar.ToString()))...
0赞 Jonathan Dodds 11/20/2023
是的。我错过了删除和创建目录。
1赞 Jonathan Dodds 11/20/2023
是否有可能文件只有文件的条目,而文件有文件和目录的条目?文件可以有“sub1\”条目而没有吗?作为实验,您可以更改为 .VS_MSBS_task.zipWinrar.zipWinrar.zipVS_MSBS_task.zipif (!Directory.Exists(des)) Directory.CreateDirectory(des);if (!Directory.Exists(des)) { Console.WriteLine($@"creating directory ""{des}"""); Directory.CreateDirectory(des); }
0赞 Pawcio 12/20/2023
是的,这确实是问题所在

答:

0赞 Pawcio 12/20/2023 #1

问题是 msbuild.zip 不包含目录条目。 通过确保父目录存在来修复此问题:

//...
if (File.Exists(des) || !des.StartsWith(destination)) continue;
var dir = Path.GetDirectoryName(des);
Directory.CreateDirectory(dir);
Console.WriteLine($@"extracting  ""{e}"" to ""{des}""");
e.ExtractToFile(des);