PowerShell执行HTTP下载的二进制文件失败:文件或目录损坏不可读
首先,结合你给出的报错信息和脚本内容,我梳理出几个核心问题,咱们一步步来解决:
1. 嵌套循环导致安装时机错误
你的脚本里把安装的foreach循环嵌套在了下载的foreach循环内部——这意味着每下载完一个软件,就会立刻尝试安装所有软件。比如刚下载完7zip,就会触发安装7zip、Notepad++和WinSCP,但这时候WinSCP的安装包可能还没开始下载,或者下载还没完成,调用不完整的文件自然会出现"损坏不可读"的报错。
修复方法
把安装循环移到下载循环的外面,确保所有软件包都下载完成后,再统一执行安装操作。
2. WinSCP安装参数无效
你给WinSCP设置的参数是 /,这是完全无效的参数。WinSCP的静默安装需要使用官方指定的参数,比如/verysilent /norestart(完全静默安装,安装完成不重启系统),错误的参数会导致安装程序异常,甚至被系统误判为文件损坏。
修复方法
修改WinSCP的Arguments为正确的静默安装参数:'/verysilent /norestart'
3. 脚本语法错误(数组元素缺失逗号)
你的$packages数组中,Notepad++和WinSCP对应的哈希表之间缺少逗号,这会导致PowerShell解析数组时出错,间接影响下载逻辑的执行。
修复方法
在Notepad++的哈希表末尾添加逗号:
@{title='Notepad++ 7.5.6';url='https://notepad-plus-plus.org/repository/7.x/7.5.6/npp.7.5.6.Installer.exe';Arguments=' /Q /S';Destination=$source}, @{title='WinScp 5.13.1';url='https://winscp.net/download/WinSCP-5.13.1-Setup.exe';Arguments=' /verysilent /norestart';Destination=$source}
4. 可选优化:替换更可靠的下载方式
原脚本使用的System.Net.WebClient在网络不稳定时容易出现下载中断,导致文件不完整。推荐改用Invoke-WebRequest,它支持自动重试,下载稳定性更好。
完整修复后的脚本
$source = 'D:\softwares' If (!(Test-Path -Path $source -PathType Container)) { New-Item -Path $source -ItemType Directory | Out-Null } $packages = @( @{title='7zip Extractor';url='http://downloads.sourceforge.net/sevenzip/7z920-x64.msi';Arguments=' /qn';Destination=$source}, @{title='Notepad++ 7.5.6';url='https://notepad-plus-plus.org/repository/7.x/7.5.6/npp.7.5.6.Installer.exe';Arguments=' /Q /S';Destination=$source}, @{title='WinScp 5.13.1';url='https://winscp.net/download/WinSCP-5.13.1-Setup.exe';Arguments=' /verysilent /norestart';Destination=$source} ) # 第一步:批量下载所有软件包 foreach ($package in $packages) { $packageName = $package.title $fileName = Split-Path $package.url -Leaf $destinationPath = Join-Path $package.Destination $fileName If (!(Test-Path -Path $destinationPath -PathType Leaf)) { Write-Host "Downloading $packageName" Invoke-WebRequest -Uri $package.url -OutFile $destinationPath -UseBasicParsing } } # 第二步:统一安装所有软件包 foreach ($package in $packages) { $packageName = $package.title $fileName = Split-Path $package.url -Leaf $destinationPath = Join-Path $package.Destination $fileName $Arguments = $package.Arguments Write-Output "Installing $packageName" # 使用Start-Process替代Invoke-Expression,更安全可控 Start-Process -FilePath $destinationPath -ArgumentList $Arguments -Wait -NoNewWindow }
另外,我把Invoke-Expression换成了Start-Process,它更适合执行外部安装程序,能更好地控制进程(比如等待安装完成、不弹出新窗口),比Invoke-Expression更安全可靠。
如果还是提示文件损坏,建议手动删除D:\softwares里的WinSCP安装包,让脚本重新下载一次,确保文件完整。
内容的提问来源于stack exchange,提问作者rashmi

