Recursively download directory tree with custom error handling
The simplest way to download a directory tree is by using Session.GetFiles
, providing path to a root of the tree as a source. This way a batch operation however stops on any error by default.
The following two approaches show how to override the default behaviour.
Particularly the first approach with Session.QueryReceived
event is relevant for other batch operations as well, including Session.PutFiles
and Session.SynchronizeDirectories
.
Advertisement
Handling Session.QueryReceived event
You can choose how an error is processed by handling Session.QueryReceived
event.
This example shows a basic implementation that outputs any errors encountered and continues.
C# Example
session.QueryReceived += (sender, e) => { Console.WriteLine("Error: {0}", e); e.Continue(); }; session.GetFiles("/home/user/*", @"d:\download\").Check();
Advertisement
VB.NET Example
AddHandler session.QueryReceived, Sub(sender As Object, e As QueryReceivedEventArgs) Console.WriteLine("Error: {0}", e.Message) e.Continue() End Sub session.GetFiles("/home/user/*", "d:\download\").Check()
PowerShell Example
$session.add_QueryReceived( { Write-Host "Error: $($_.Message)" $_.Continue() } ) $session.GetFiles("/home/user/*", "d:\download\").Check()
Upload
The same mechanism can be used for uploads with Session.PutFiles
or synchronization with Session.SynchronizeDirectories
.
Explicit Implementation of a File Tree Download
In case you need even more control over a download process, you can implement walking of a directory tree explicitly and handle each file individually, as you need.
This example shows a basic implementation that outputs any errors encountered and continues.
C# Example
using System; using System.Collections.Generic; using System.IO; using WinSCP; class Example { public static int Main() { try { // Setup session options SessionOptions sessionOptions = new SessionOptions { Protocol = Protocol.Sftp, HostName = "example.com", UserName = "user", Password = "mypassword", SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx..." }; string localPath = @"d:\backup"; string remotePath = "/home/user"; using (Session session = new Session()) { // Connect session.Open(sessionOptions); // Enumerate files and directories to download var opts = WinSCP.EnumerationOptions.EnumerateDirectories | WinSCP.EnumerationOptions.AllDirectories; IEnumerable<RemoteFileInfo> fileInfos = session.EnumerateRemoteFiles(remotePath, null, opts); foreach (RemoteFileInfo fileInfo in fileInfos) { string localFilePath = RemotePath.TranslateRemotePathToLocal( fileInfo.FullName, remotePath, localPath); if (fileInfo.IsDirectory) { // Create local subdirectory, if it does not exist yet if (!Directory.Exists(localFilePath)) { Directory.CreateDirectory(localFilePath); } } else { Console.WriteLine("Downloading file {0}...", fileInfo.FullName); // Download file string remoteFilePath = RemotePath.EscapeFileMask(fileInfo.FullName); TransferOperationResult transferResult = session.GetFiles(remoteFilePath, localFilePath); // Did the download succeeded? if (!transferResult.IsSuccess) { // Print error (but continue with other files) Console.WriteLine( "Error downloading file {0}: {1}", fileInfo.FullName, transferResult.Failures[0].Message); } } } } return 0; } catch (Exception e) { Console.WriteLine("Error: {0}", e); return 1; } } }
Advertisement
PowerShell Example
param ( # Use Generate Session URL function to obtain a value for -sessionUrl parameter. [Parameter(Mandatory = $True)] $sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xxxxxxxxxxx...@example.com/", [Parameter(Mandatory = $True)] $remotePath, [Parameter(Mandatory = $True)] $localPath ) try { # Load WinSCP .NET assembly Add-Type -Path "WinSCPnet.dll" # Setup session options from URL $sessionOptions = New-Object WinSCP.SessionOptions $sessionOptions.ParseUrl($sessionUrl) $session = New-Object WinSCP.Session $session.SessionLogPath = "session.log" try { # Connect $session.Open($sessionOptions) # Enumerate files and directories to download $fileInfos = $session.EnumerateRemoteFiles( $remotePath, $Null, ([WinSCP.EnumerationOptions]::EnumerateDirectories -bor [WinSCP.EnumerationOptions]::AllDirectories)) foreach ($fileInfo in $fileInfos) { $localFilePath = [WinSCP.RemotePath]::TranslateRemotePathToLocal( $fileInfo.FullName, $remotePath, $localPath) if ($fileInfo.IsDirectory) { # Create local subdirectory, if it does not exist yet if (!(Test-Path $localFilePath)) { New-Item $localFilePath -ItemType directory | Out-Null } } else { Write-Host "Downloading file $($fileInfo.FullName)..." # Download file $remoteFilePath = [WinSCP.RemotePath]::EscapeFileMask($fileInfo.FullName) $transferResult = $session.GetFiles($remoteFilePath, $localFilePath) # Did the download succeeded? if (!$transferResult.IsSuccess) { # Print error (but continue with other files) Write-Host ( "Error downloading file $($fileInfo.FullName): " + "$($transferResult.Failures[0].Message)") } } } } finally { # Disconnect, clean up $session.Dispose() } exit 0 } catch { Write-Host "Error: $($_.Exception.Message)" exit 1 }
Advertisement
Upload
For an example of walking a local tree to upload files individually, see Recursively move files in directory tree to/from SFTP/FTP server while preserving source directory structure.
The upload example calls OperationResultBase.Check
, so it aborts on any error. Just replace the call with OperationResultBase.IsSuccess
test, as the download example above does. See Capturing results of operations.