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. If you need to use a custom error handling, for example to ignore any error and just continue, you need to implement walking the tree explicitly.

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 xx:xx:xx:xx:xx:xx:xx:xx..."
            };
 
            string localPath = @"d:\backup";
            string remotePath = "/home/user";
 
            using (Session session = new Session())
            {
                // Connect
                session.Open(sessionOptions);
 
                // Enumerate files and directories to download
                IEnumerable<RemoteFileInfo> fileInfos =
                    session.EnumerateRemoteFiles(
                        remotePath, null,
                        EnumerationOptions.EnumerateDirectories |
                            EnumerationOptions.AllDirectories);
 
                foreach (RemoteFileInfo fileInfo in fileInfos)
                {
                    string localFilePath =
                        session.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
                        TransferOperationResult transferResult =
                            session.GetFiles(
                                session.EscapeFileMask(fileInfo.FullName), 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;
        }
    }
}

PowerShell Example

param (
    # Use Generate URL function to obtain a value for -sessionUrl parameter.
    [Parameter(Mandatory = $True)]
    $sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xx-xx-xx@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 =
                $session.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
                $transferResult =
                    $session.GetFiles(
                        $session.EscapeFileMask($fileInfo.FullName), $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
}

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.

Advertisement

The upload example calls OperationResultBase.Check, so it aborts on any error. Just replace the call with OperationResultBase.IsSuccess test as this example does. See Capturing Results of Operations