You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

提取ZipArchive时触发UnauthorizedAccessException的问题排查

问题描述

我正在开发一个C#控制台应用程序,从GitHub发布页面下载.zip压缩包并提取到本地指定路径。下载过程正常,但提取时出现权限拒绝异常。

相关代码

client.DownloadFile("RELEASE-URL-HERE", "Release.zip");
using (ZipArchive archive = ZipFile.OpenRead("Release.zip"))
{
    foreach (ZipArchiveEntry entry in archive.Entries)
    {
        Console.WriteLine("Extracting " + entry.Name);                    
        entry.ExtractToFile(Path.Combine(@"E:\dev\winforms\pie\bin\Release", entry.Name));
    }
}

触发的异常信息

System.UnauthorizedAccessException: Access to the path 'E:\dev\winforms\pie\bin\Release' is denied.
   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 pie.PieUpdater.Main()

我知道有更简便的批量提取方法,但希望通过foreach循环实现对每个文件提取的控制(如过滤文件、记录特定类型文件)。更换到空目录、以管理员身份运行程序都无法解决问题,怀疑是把zip内的目录当作文件处理导致的错误。想确认目录处理方式是否错误,是否需要手动创建目录,或者有没有无需单独处理目录也能实现循环控制提取的方法。


解决方案

你的判断完全正确,问题核心是ZipArchiveEntry中包含目录项,而ExtractToFile仅能处理文件,直接对目录调用该方法会触发权限异常(系统不允许将目录当作文件创建)。

1. 手动处理目录的标准实现

循环中先判断当前entry是否为目录,若是则先创建对应本地目录,再处理文件:

string targetDir = @"E:\dev\winforms\pie\bin\Release";
client.DownloadFile("RELEASE-URL-HERE", "Release.zip");

using (ZipArchive archive = ZipFile.OpenRead("Release.zip"))
{
    foreach (ZipArchiveEntry entry in archive.Entries)
    {
        // 拼接完整目标路径,必须用FullName保留zip内的目录结构
        string destinationPath = Path.Combine(targetDir, entry.FullName);

        // 判断是否为目录:zip内的目录项FullName通常以/或\结尾,或长度为0
        if (entry.FullName.EndsWith("/") || entry.FullName.EndsWith("\\") || entry.Length == 0)
        {
            Directory.CreateDirectory(destinationPath);
            Console.WriteLine("Created directory: " + entry.FullName);
            continue;
        }

        // 确保文件的父目录存在(防止文件在子目录但目录未创建)
        Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
        
        // 提取文件,添加overwrite参数避免文件已存在的异常
        entry.ExtractToFile(destinationPath, overwrite: true);
        Console.WriteLine("Extracted file: " + entry.Name);
    }
}
  • 用entry.FullName而非entry.Name:Name仅返回最后一级名称,FullName包含完整相对路径,能正确还原目录结构。
  • 目录判断逻辑:结合路径结尾符和文件长度双重判断,避免误判。

2. 无需单独处理目录的简化实现(保留循环控制)

如果不想手动处理目录,可跳过空目录项,提取文件前自动创建父目录即可:

string targetDir = @"E:\dev\winforms\pie\bin\Release";
client.DownloadFile("RELEASE-URL-HERE", "Release.zip");

using (ZipArchive archive = ZipFile.OpenRead("Release.zip"))
{
    foreach (ZipArchiveEntry entry in archive.Entries)
    {
        // 跳过空目录项(目录项的Name为空)
        if (string.IsNullOrEmpty(entry.Name))
            continue;

        string destinationPath = Path.Combine(targetDir, entry.FullName);
        // 自动创建文件所需的所有父目录
        Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
        
        // 这里可添加自定义过滤逻辑,比如只提取指定类型文件
        if (entry.Name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
        {
            entry.ExtractToFile(destinationPath, overwrite: true);
            Console.WriteLine("Extracted DLL: " + entry.Name);
        }
        else if (entry.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
        {
            // 针对EXE文件的自定义操作
        }
    }
}

这种方式下,目录项会被自动跳过,提取文件时Directory.CreateDirectory会自动生成所有必要的父目录,无需单独处理目录逻辑,同时保留了循环控制的灵活性。

关键注意事项

  • 必须使用entry.FullName拼接路径,否则所有文件会被提取到根目标目录,丢失原有结构。
  • 建议添加overwrite: true参数,避免因目标文件已存在抛出异常(可根据需求调整)。
  • 过滤逻辑可灵活扩展,比如根据文件路径、大小、扩展名等条件筛选需要处理的条目。

内容的提问来源于stack exchange,提问作者Mario Mateaș

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:41:00