Загрузка файлов из набора папок на FTP / SFTP-сервер с помощью WinSCP. NET сборка в PowerShell и результаты электронной почты - PullRequest
1 голос
/ 28 января 2020

У меня есть сценарий PowerShell, над которым я работаю. CSV-файл импортируется, чтобы получить исходный и целевой путь. Цель состоит в том, чтобы переместить файлы с сервера SFTP / FTP в место назначения и отправить отчет по электронной почте.

Планировщик задач будет запускать этот код каждый час. А если есть новый файл, то по электронной почте будет отправлено письмо.

Это почти сделано, но отсутствуют две вещи:

  • Проверьте, существует ли файл, и Body электронная почта кажется пустой: появляется следующая ошибка: невозможно проверить аргумент в параметре «Body» Аргумент нулевой или пустой. Укажите аргумент, который не является пустым или пустым, а затем повторите команду.

  • Я хотел бы получить некоторую помощь о том, как проверить, существует ли файл и как получить это письмо, если новый файл был удален и скопирован в список адресатов

$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)"
}

Ответы [ 2 ]

1 голос
/ 29 января 2020

Я считаю, что у вашего кода больше проблем, чем вы думаете.

  • Ваша комбинация SynchronizeDirectories и GetFiles является подозрительной. Сначала вы загружаете только новые файлы SynchronizeDirectories, а затем загружаете все файлы GetFiles. Я не думаю, что вы этого хотите.
  • При любой ошибке вызовет .Check вызов, и вы не будете записывать ошибку в свой отчет.
  • Вы продолжаете отправлять частичные отчеты по Send-MailMessage в foreach l oop

Это мой взгляд на вашу проблему, надеюсь, я правильно понял, что вы хотите реализовать:

$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
1 голос
/ 28 января 2020

Чтобы проверить и убедиться, что файл csv существует до того, как вы обработаете все это, вы можете использовать Test-Path,

    ...
    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 она исходит из наконец, l oop, потому что есть случаи, когда $SMTPBody будет нулевым. Это больше не будет проблемой, потому что $ SMTPBody будет иметь некоторый текст, когда файл не найден в начале.

Даже если вы используете return в операторе if, чтобы проверить, существует ли файл, 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 оператор отправляет электронное письмо с пустым телом, потому что оно никогда не было установлено (из-за исключения). Я бы порекомендовал использовать отладчик (шаг через) и посмотреть, какой шаг вызывает исключение, и посмотреть, как добавить шаги, чтобы убедиться, что скрипт не дает сбоя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...