Watching for changes in SFTP/FTP server
Neither SFTP nor FTP protocol have any mechanism to notify a client about changes in a remote folder. The only solution to detect changes is to periodically enumerate remote directory tree and find differences. It’s easy to implement with use of Session.EnumerateRemoteFiles
method and Compare-Object
cmdlet in PowerShell or Enumerable.Except
LINQ method in C#/VB.NET.
Note that, if you want to download the changes, you do not need to check for changes yourself, just run Session.SynchronizeDirectories
in a loop. You can use the Keep local directory up to date extension.
Advertisement
PowerShell
param ( # Use Generate Session URL function to obtain a value for -sessionUrl parameter. $sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xxxxxxxxxxx...@example.com/", $remotePath = "/home/user" ) try { # Load WinSCP .NET assembly Add-Type -Path "WinSCPnet.dll" # Setup session options $sessionOptions = New-Object WinSCP.SessionOptions $sessionOptions.ParseUrl($sessionUrl) $session = New-Object WinSCP.Session try { # Connect $session.Open($sessionOptions) $prevFiles = $Null; while ($True) { # Collect file list $files = $session.EnumerateRemoteFiles( $remotePath, "*.*", [WinSCP.EnumerationOptions]::AllDirectories) | Select-Object -ExpandProperty FullName if ($prevFiles -eq $Null) { # In the first round, just print number of files found Write-Host "Found $($files.Count) files" } else { # Then look for differences against the previous list $changes = Compare-Object -DifferenceObject $files -ReferenceObject $prevfiles $added = $changes | Where-Object -FilterScript { $_.SideIndicator -eq "=>" } | Select-Object -ExpandProperty InputObject if ($added) { Write-Host "Added files:" Write-Host ($added -Join "`n") } $removed = $changes | Where-Object -FilterScript { $_.SideIndicator -eq "<=" } | Select-Object -ExpandProperty InputObject if ($removed) { Write-Host "Removed files:" Write-Host ($removed -Join "`n") } } $prevFiles = $files Write-Host "Sleeping 10s..." Start-Sleep -Seconds 10 } } finally { # Disconnect, clean up $session.Dispose() } } catch { Write-Host "Error: $($_.Exception.Message)" exit 1 }
Advertisement
C#
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using WinSCP; class Program { static int Main(string[] args) { try { // Setup session options SessionOptions sessionOptions = new SessionOptions { Protocol = Protocol.Sftp, HostName = "example.com", UserName = "user", Password = "password", SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx...", }; const string remotePath = "/home/user"; using (Session session = new Session()) { // Connect session.Open(sessionOptions); List<string> prevFiles = null; while (true) { // Collect file list List<string> files = session.EnumerateRemoteFiles( remotePath, "*.*", EnumerationOptions.AllDirectories) .Select(fileInfo => fileInfo.FullName) .ToList(); if (prevFiles == null) { // In the first round, just print number of files found Console.WriteLine("Found {0} files", files.Count); } else { // Then look for differences against the previous list IEnumerable<string> added = files.Except(prevFiles); if (added.Any()) { Console.WriteLine("Added files:"); foreach (string path in added) { Console.WriteLine(path); } } IEnumerable<string> removed = prevFiles.Except(files); if (removed.Any()) { Console.WriteLine("Removed files:"); foreach (string path in removed) { Console.WriteLine(path); } } } prevFiles = files; Console.WriteLine("Sleeping 10s..."); Thread.Sleep(10000); } } } catch (Exception e) { Console.WriteLine("Error: {0}", e); return 1; } } }
Advertisement