使用 PowerShell 中的 WinSCP .NET 程序集和电子邮件结果从 FTP/SFTP 服务器上的一组文件夹下载文件
Download files from set of folders on FTP/SFTP server with WinSCP .NET assembly in PowerShell and email results
我正在处理这个 PowerShell 脚本。导入 CSV 文件以获取源路径和目标路径。目标是将文件从 SFTP/FTP 服务器移动到目的地并发送电子邮件报告。
任务计划程序将每小时 运行 此代码。如果有新文件,将发送电子邮件。
快完成了,但是缺少两件事:
检查文件是否已经存在,正文电子邮件是否为空:出现以下错误:无法验证参数 'Body' 上的参数。参数为 null 或空。提供一个不是的论点
null 或为空,然后重试该命令。
我需要一些帮助,了解如何检查文件是否存在以及如果新文件被删除并复制到目标列表时如何获取此电子邮件
$SMTPBody = ""
$SMTPMessage = @{
"SMTPServer" = ""
"From" = ""
"To" = ""
"Subject" = "New File"
}
try {
# Load WinSCP .NET assembly
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = [WinSCP.Protocol]::sftp
HostName = ""
UserName = ""
Password = ""
PortNumber = "22"
FTPMode = ""
GiveUpSecurityAndAcceptAnySshHostKey = $true
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
Import-Csv -Path "D:\FILESOURCE.csv" -ErrorAction Stop | foreach {
$synchronizationResult = $session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, $_.Destination, $_.Source, $False)
$synchronizationResult.Check()
foreach ($download in $synchronizationResult.Downloads ) {
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
$SMTPBody +=
"`n Files: $($download.FileName -join ', ') `n" +
"Current Location: $($_.Destination)`n"
Send-MailMessage @SMTPMessage -Body $SMTPBody
}
$transferResult =
$session.GetFiles($_.Source, $_.Destination, $False, $transferOptions)
#Find the latest downloaded file
$latestTransfer =
$transferResult.Transfers |
Sort-Object -Property @{ Expression = { (Get-Item $_.Destination).LastWriteTime }
} -Descending |Select-Object -First 1
}
if ($latestTransfer -eq $Null) {
Write-Host "No files found."
$SMTPBody += "There are no new files at the moment"
}
else
{
$lastTimestamp = (Get-Item $latestTransfer.Destination).LastWriteTime
Write-Host (
"Downloaded $($transferResult.Transfers.Count) files, " +
"latest being $($latestTransfer.FileName) with timestamp $lastTimestamp.")
$SMTPBody += "file : $($latestTransfer)"
}
Write-Host "Waiting..."
Start-Sleep -Seconds 5
}
finally
{
Send-MailMessage @SMTPMessage -Body $SMTPBody
# Disconnect, clean up
$session.Dispose()
}
}
catch
{
Write-Host "Error: $($_.Exception.Message)"
}
要在处理整个文件之前检查并确保 csv 文件存在,您可以使用测试路径,
...
if (!(Test-Path D:\FileSource.csv)) {
Write-Output "No File Found"
$SMTPBody += "There are no new files at the moment"
return; # Dont run. file not found. Exit out.
}
Import-Csv -Path "D:\FILESOURCE.csv" -ErrorAction Stop | foreach {
...
并且对于 Body 错误,它来自 finally 循环,因为在某些情况下 $SMTPBody
将为空。这将不再是一个问题,因为如果在开头找不到文件,$SMTPBody 将有一些文本。
即使您在 if 语句中使用 return
来检查文件是否存在,finally
也会始终被执行。由于我们更新了 $smtpbody,您的 Send-MailMessage
将不再出错。
更新
如果你想检查你正在下载的文件是否已经存在,你可以像这样使用if语句,
foreach ($download in $synchronizationResult.Downloads ) {
if (!(Test-Path Join-Path D: $download.FileName) {
$SMTPBody += "File $($download.Filename) already exists, skipping."
continue # will go to the next download...
}
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
...
如果您确实收到有关正文的错误,那主要是因为您的脚本遇到异常并直接发送到 finally
语句。 Finally
语句发送带有空正文的电子邮件,因为它从未被设置(由于异常)。我建议使用调试器(单步执行)并查看是哪一步导致了异常,并考虑添加步骤以确保脚本不会失败。
我相信您的代码存在的问题比您想象的要多。
- 您的
SynchronizeDirectories
和 GetFiles
组合很可疑。您首先通过 SynchronizeDirectories
下载新文件,然后通过 GetFiles
下载所有文件。我不认为你想要那样。
- 如果出现任何错误,
.Check
调用将抛出并且您不会将错误收集到您的报告中。
- 您在 foreach 循环中
Send-MailMessage
继续发送部分报告
这是我对你的问题的看法,希望我已经正确理解你想要实施的内容:
$SMTPBody = ""
Import-Csv -Path "FILESOURCE.csv" -ErrorAction Stop | foreach {
Write-Host "$($_.Source) => $($_.Destination)"
$SMTPBody += "$($_.Source) => $($_.Destination)`n"
$synchronizationResult =
$session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, $_.Destination, $_.Source, $False)
$downloaded = @()
$failed = @()
$latestName = $Null
$latest = $Null
foreach ($download in $synchronizationResult.Downloads)
{
if ($download.Error -eq $Null)
{
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
$downloaded += $download.FileName
$ts = (Get-Item $download.Destination).LastWriteTime
if ($ts -gt $latest)
{
$latestName = $download.FileName;
$latest = $ts
}
}
else
{
Write-Host "File $($download.FileName) download failed" -ForegroundColor Red
$failed += $download.FileName
}
}
if ($downloaded.Count -eq 0)
{
$SMTPBody += "No new files were downloaded`n"
}
else
{
$SMTPBody +=
"Downloaded $($downloaded.Count) files:`n" +
($downloaded -join ", ") + "`n" +
"latest being $($latestName) with timestamp $latest.`n"
}
if ($failed.Count -gt 0)
{
$SMTPBody +=
"Failed to download $($failed.Count) files:`n" +
($failed -join ", ") + "`n"
}
$SMTPBody += "`n"
}
它会给你这样的报告:
/source1 => C:\dest1`
Downloaded 3 files:
/source1/aaa.txt, /source1/bbb.txt, /source1/ccc.txt
latest being /source1/ccc.txt with timestamp 01/29/2020 07:49:07.
/source2 => C:\dest2
Downloaded 1 files:
/source2/aaa.txt
latest being /source2/aaa.txt with timestamp 01/29/2020 07:22:37.
Failed to download 1 files:
/source2/bbb.txt
我正在处理这个 PowerShell 脚本。导入 CSV 文件以获取源路径和目标路径。目标是将文件从 SFTP/FTP 服务器移动到目的地并发送电子邮件报告。
任务计划程序将每小时 运行 此代码。如果有新文件,将发送电子邮件。
快完成了,但是缺少两件事:
检查文件是否已经存在,正文电子邮件是否为空:出现以下错误:无法验证参数 'Body' 上的参数。参数为 null 或空。提供一个不是的论点 null 或为空,然后重试该命令。
我需要一些帮助,了解如何检查文件是否存在以及如果新文件被删除并复制到目标列表时如何获取此电子邮件
$SMTPBody = ""
$SMTPMessage = @{
"SMTPServer" = ""
"From" = ""
"To" = ""
"Subject" = "New File"
}
try {
# Load WinSCP .NET assembly
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = [WinSCP.Protocol]::sftp
HostName = ""
UserName = ""
Password = ""
PortNumber = "22"
FTPMode = ""
GiveUpSecurityAndAcceptAnySshHostKey = $true
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
Import-Csv -Path "D:\FILESOURCE.csv" -ErrorAction Stop | foreach {
$synchronizationResult = $session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, $_.Destination, $_.Source, $False)
$synchronizationResult.Check()
foreach ($download in $synchronizationResult.Downloads ) {
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
$SMTPBody +=
"`n Files: $($download.FileName -join ', ') `n" +
"Current Location: $($_.Destination)`n"
Send-MailMessage @SMTPMessage -Body $SMTPBody
}
$transferResult =
$session.GetFiles($_.Source, $_.Destination, $False, $transferOptions)
#Find the latest downloaded file
$latestTransfer =
$transferResult.Transfers |
Sort-Object -Property @{ Expression = { (Get-Item $_.Destination).LastWriteTime }
} -Descending |Select-Object -First 1
}
if ($latestTransfer -eq $Null) {
Write-Host "No files found."
$SMTPBody += "There are no new files at the moment"
}
else
{
$lastTimestamp = (Get-Item $latestTransfer.Destination).LastWriteTime
Write-Host (
"Downloaded $($transferResult.Transfers.Count) files, " +
"latest being $($latestTransfer.FileName) with timestamp $lastTimestamp.")
$SMTPBody += "file : $($latestTransfer)"
}
Write-Host "Waiting..."
Start-Sleep -Seconds 5
}
finally
{
Send-MailMessage @SMTPMessage -Body $SMTPBody
# Disconnect, clean up
$session.Dispose()
}
}
catch
{
Write-Host "Error: $($_.Exception.Message)"
}
要在处理整个文件之前检查并确保 csv 文件存在,您可以使用测试路径,
...
if (!(Test-Path D:\FileSource.csv)) {
Write-Output "No File Found"
$SMTPBody += "There are no new files at the moment"
return; # Dont run. file not found. Exit out.
}
Import-Csv -Path "D:\FILESOURCE.csv" -ErrorAction Stop | foreach {
...
并且对于 Body 错误,它来自 finally 循环,因为在某些情况下 $SMTPBody
将为空。这将不再是一个问题,因为如果在开头找不到文件,$SMTPBody 将有一些文本。
即使您在 if 语句中使用 return
来检查文件是否存在,finally
也会始终被执行。由于我们更新了 $smtpbody,您的 Send-MailMessage
将不再出错。
更新
如果你想检查你正在下载的文件是否已经存在,你可以像这样使用if语句,
foreach ($download in $synchronizationResult.Downloads ) {
if (!(Test-Path Join-Path D: $download.FileName) {
$SMTPBody += "File $($download.Filename) already exists, skipping."
continue # will go to the next download...
}
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
...
如果您确实收到有关正文的错误,那主要是因为您的脚本遇到异常并直接发送到 finally
语句。 Finally
语句发送带有空正文的电子邮件,因为它从未被设置(由于异常)。我建议使用调试器(单步执行)并查看是哪一步导致了异常,并考虑添加步骤以确保脚本不会失败。
我相信您的代码存在的问题比您想象的要多。
- 您的
SynchronizeDirectories
和GetFiles
组合很可疑。您首先通过SynchronizeDirectories
下载新文件,然后通过GetFiles
下载所有文件。我不认为你想要那样。 - 如果出现任何错误,
.Check
调用将抛出并且您不会将错误收集到您的报告中。 - 您在 foreach 循环中
Send-MailMessage
继续发送部分报告
这是我对你的问题的看法,希望我已经正确理解你想要实施的内容:
$SMTPBody = ""
Import-Csv -Path "FILESOURCE.csv" -ErrorAction Stop | foreach {
Write-Host "$($_.Source) => $($_.Destination)"
$SMTPBody += "$($_.Source) => $($_.Destination)`n"
$synchronizationResult =
$session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, $_.Destination, $_.Source, $False)
$downloaded = @()
$failed = @()
$latestName = $Null
$latest = $Null
foreach ($download in $synchronizationResult.Downloads)
{
if ($download.Error -eq $Null)
{
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
$downloaded += $download.FileName
$ts = (Get-Item $download.Destination).LastWriteTime
if ($ts -gt $latest)
{
$latestName = $download.FileName;
$latest = $ts
}
}
else
{
Write-Host "File $($download.FileName) download failed" -ForegroundColor Red
$failed += $download.FileName
}
}
if ($downloaded.Count -eq 0)
{
$SMTPBody += "No new files were downloaded`n"
}
else
{
$SMTPBody +=
"Downloaded $($downloaded.Count) files:`n" +
($downloaded -join ", ") + "`n" +
"latest being $($latestName) with timestamp $latest.`n"
}
if ($failed.Count -gt 0)
{
$SMTPBody +=
"Failed to download $($failed.Count) files:`n" +
($failed -join ", ") + "`n"
}
$SMTPBody += "`n"
}
它会给你这样的报告:
/source1 => C:\dest1`
Downloaded 3 files:
/source1/aaa.txt, /source1/bbb.txt, /source1/ccc.txt
latest being /source1/ccc.txt with timestamp 01/29/2020 07:49:07.
/source2 => C:\dest2
Downloaded 1 files:
/source2/aaa.txt
latest being /source2/aaa.txt with timestamp 01/29/2020 07:22:37.
Failed to download 1 files:
/source2/bbb.txt