使用 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 服务器移动到目的地并发送电子邮件报告。

任务计划程序将每小时 运行 此代码。如果有新文件,将发送电子邮件。

快完成了,但是缺少两件事:

$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 语句发送带有空正文的电子邮件,因为它从未被设置(由于异常)。我建议使用调试器(单步执行)并查看是哪一步导致了异常,并考虑添加步骤以确保脚本不会失败。

我相信您的代码存在的问题比您想象的要多。

  • 您的 SynchronizeDirectoriesGetFiles 组合很可疑。您首先通过 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