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

PowerShell压缩脚本无法保留目录结构且报文件未找到错误

解决PowerShell自定义压缩脚本的两个问题

一、修复「File not found」错误

错误原因

  1. Test.ps1调用语法错误:原调用代码Compress-CompatibleArchive (args[1]) (args[2])未给args加$符号,PowerShell无法识别为自动变量,导致参数传递失效,实际传入的路径无效;同时参数索引错误,命令行第一个参数是脚本本身,源路径应为$args[0]、目标路径为$args[1]。
  2. 脚本未支持目录处理:原ValidatePaths函数仅通过FileInfo判断路径,若传入目录,后续用FileStream打开会触发错误,且脚本未递归处理目录内的文件。

修复步骤

  1. 修正Test.ps1调用代码:
Compress-CompatibleArchive $args[0] $args[1]
  1. 更新ValidatePaths函数:支持验证文件或目录路径的存在性:
function ValidatePaths([String[]] $paths) { 
    foreach ($f in $paths) { 
        if (-not (Test-Path -Path $f -PathType Any)) { 
            throw [FileNotFoundException] "File or directory not found ($f)"
        } 
    }
}

二、修复Zip文件不保留目录结构问题

问题原因

原脚本仅使用[Path]::GetFileName($file)作为Zip条目名称,所有文件都会被压缩到Zip根目录,丢失原有目录层级。需要为每个文件生成相对于源路径的相对路径,作为Zip条目名称。

修复步骤

  1. 添加递归遍历目录的逻辑,获取目录下所有子文件;
  2. 计算每个文件相对于源路径的相对路径,作为Zip条目名称,保留原结构。

修改后的完整脚本

using namespace System.IO
using namespace System.IO.Compression

function Compress-CompatibleArchive {
    param (
        [String[]] $Path,
        [String] $DestinationPath,
        [ValidateSet("Fastest", "NoCompression", "Optimal", "Maximum")]
        [String] $CompressionLevel = "Optimal"
    )
    ValidatePaths($Path)
    ValidateDestinationPath($DestinationPath)

    # 解析压缩级别
    $level = switch ($CompressionLevel) {
        "Fastest" { [CompressionLevel]::Fastest }
        "Optimal" { [CompressionLevel]::Optimal }
        "Maximum" { [CompressionLevel]::SmallestSize }
        default { [CompressionLevel]::NoCompression }
    }

    $outputStream = [FileStream]::new($DestinationPath, [FileMode]::Create, [FileAccess]::Write)
    try {
        $archive = [Compression.ZipArchive]::new($outputStream, [Compression.ZipArchiveMode]::Create)
        try {
            foreach ($itemPath in $Path) {
                $item = Get-Item -Path $itemPath
                $rootPath = $item.FullName

                # 处理目录或文件
                if ($item.PSIsContainer) {
                    # 递归获取目录下所有文件
                    $files = Get-ChildItem -Path $rootPath -Recurse -File
                    foreach ($file in $files) {
                        # 计算相对路径作为Zip条目名称
                        $relativePath = $file.FullName.Substring($rootPath.Length + 1)
                        $entry = $archive.CreateEntry($relativePath, $level)
                        CopyFileToArchiveEntry $file.FullName $entry
                    }
                }
                else {
                    # 处理单个文件
                    $entry = $archive.CreateEntry($item.Name, $level)
                    CopyFileToArchiveEntry $item.FullName $entry
                }
            }
        }
        finally {
            $archive.Dispose()
        }
    }
    finally {
        $outputStream.Dispose()
    }
}

# 辅助函数:复制文件到Zip条目
function CopyFileToArchiveEntry([String]$filePath, [ZipArchiveEntry]$entry) {
    $entryStream = $entry.Open()
    try {
        $inputStream = [FileStream]::new($filePath, [FileMode]::Open, [FileAccess]::Read)
        try {
            $inputStream.CopyTo($entryStream)
        }
        finally {
            $inputStream.Dispose()
        }
    }
    finally {
        $entryStream.Dispose()
    }
}

function ValidatePaths([String[]] $paths) { 
    foreach ($f in $paths) { 
        if (-not (Test-Path -Path $f -PathType Any)) { 
            throw [FileNotFoundException] "File or directory not found ($f)"
        } 
    }
}

function ValidateDestinationPath { 
    param (
        [String] $destinationPath
    ) 
    
    $di = [DirectoryInfo]::new($destinationPath) 
    
    if ($di.Exists -or [Path]::EndsInDirectorySeparator($destinationPath)) { 
        throw [IOException] "DestinationPath cannot be a directory ($destinationPath)" 
    } 
    
    $fi = [FileInfo]::new($destinationPath)
    
    if ($fi.Exists) { 
        throw [IOException] "File already exists ($destinationPath)"
    }
}

Export-ModuleMember -Function Compress-CompatibleArchive

关键修改说明

  1. 添加默认压缩级别:为$CompressionLevel设置默认值Optimal,提升易用性;
  2. 优化压缩级别解析:用switch替代原if-else链,代码更简洁;
  3. 递归处理目录:通过Get-ChildItem -Recurse -File获取目录下所有文件,避免遗漏子文件;
  4. 保留目录结构:计算文件相对路径作为Zip条目名称,确保压缩后层级与原结构一致;
  5. 资源安全释放:用try-finally块确保流和ZipArchive资源被正确释放,避免文件句柄泄漏;
  6. 提取辅助函数:将文件复制逻辑独立,减少代码重复,提升可维护性。

测试验证

  1. 确保Test.ps1内容正确;
  2. 命令行调用示例:
./Test.ps1 "C:\Users\USER\OneDrive\Pictures\testing" "C:\output\test.zip"

此时脚本会正确压缩目录内所有文件并保留原结构,且不会触发「File not found」错误。

内容的提问来源于stack exchange,提问作者Royston

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 02:24:52