Differences

This shows you the differences between the selected revisions of the page.

library_example_recursive_download_custom_error_handling 2015-07-09 library_example_recursive_download_custom_error_handling 2022-06-16 (current)
Line 1: Line 1:
====== Recursively download directory tree with custom error handling ====== ====== Recursively download directory tree with custom error handling ======
-The simplest way to download a directory tree is by using ''[[library_session_getfiles|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.+The simplest way to download a directory tree is by using ''[[library_session_getfiles|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 ''[[library_session_queryreceived|Session.QueryReceived]]'' event is relevant for other batch operations as well, including ''[[library_session_putfiles|Session.PutFiles]]'' and ''[[library_session_synchronizedirectories|Session.SynchronizeDirectories]]''. 
 + 
 +===== [[queryreceived]] Handling Session.QueryReceived event ===== 
 + 
 +You can choose how an error is processed by handling ''[[library_session_queryreceived|Session.QueryReceived]]'' event.
This example shows a basic implementation that outputs any errors encountered and continues. This example shows a basic implementation that outputs any errors encountered and continues.
-The example uses [[library|WinSCP .NET assembly]] from a [[library_powershell|PowerShell]] script. If you have another preferred language, you can easily translate it.+==== C# Example ==== 
 + 
 +<code csharp> 
 +session.QueryReceived += (sender, e) => 
 +
 + ···Console.WriteLine(&quot;Error: {0}", e); 
 +    e.Continue(); 
 +}; 
 + 
 +session.GetFiles("/home/user/*", @"d:\download\").Check(); 
 +</code> 
 + 
 +==== VB.NET Example ==== 
 + 
 +<code vbnet> 
 +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() 
 +</code> 
 + 
 +==== [[event_powershell]] PowerShell Example ====
<code powershell> <code powershell>
-param ( +$session.add_QueryReceived( {  
-    [Parameter(Mandatory)] +    Write-Host "Error: $($_.Message)" 
-    $sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xx-xx-xx@example.com/&quot;, +    $_.Continue() 
- ···[Parameter(Mandatory)] +}
- ···$remotePath, + 
- ···[Parameter(Mandatory)] +$session.GetFiles("/home/user/*&quot;, &quot;d:\download\&quot;).Check() 
- ···$localPath +</code&gt; 
-) + 
-  +==== Upload ==== 
-function DownloadDirectory ($session, $localPath, $remotePath)+ 
 +The same mechanism can be used for uploads with ''[[library_session_putfiles|Session.PutFiles]]'' or synchronization with &#039;'[[library_session_synchronizedirectories|Session.SynchronizeDirectories]]''. 
 + 
 + 
 +===== [[tree_download]] 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 ==== 
 + 
 +<code csharp> 
 +using System; 
 +using System.Collections.Generic; 
 +using System.IO; 
 +using WinSCP; 
 + 
 +class Example
{ {
-    Write-Host ("Downloading directory {0} ..." -f $remotePath) +    public static int Main()
-  +
-    try+
    {     {
-        $directoryInfo = $session.ListDirectory($remotePath) +        try
- +
-        foreach ($fileInfo in $directoryInfo.Files)+
        {         {
-            $remoteFilePath = ($remotePath + "/" + $fileInfo.Name)  +            // Setup session options 
-············ +            SessionOptions sessionOptions = new SessionOptions
-            if ($fileInfo.IsDirectory)+
            {             {
-                # Skip references to current and parent directories +                Protocol = Protocol.Sftp, 
-                if (($fileInfo.Name -ne ".") -and +               HostName = "example.com", 
- ···················($fileInfo.Name -ne "..&quot;))+               UserName = "user", 
 +                Password = &quot;mypassword&quot;, 
 + ···············SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx..." 
 + ···········}; 
 + 
 + ···········string localPath = @&quot;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&lt;RemoteFileInfo> fileInfos = 
 +                    session.EnumerateRemoteFiles(remotePath, null, opts)
 + 
 +                foreach (RemoteFileInfo fileInfo in fileInfos)
                {                 {
-                    # Recurse into subdirectories +                    string localFilePath = 
- ···················$localDirPath = ($localPath + &quot;\&quot; + $fileInfo.Name)+ ·······················RemotePath.TranslateRemotePathToLocal( 
 + ···························fileInfo.FullName, remotePath, localPath);
-                    # Create local subdirectory, if it does not exist yet +                    if (fileInfo.IsDirectory)
-····················if (!(Test-Path $localDirPath))+
                    {                     {
-                        New-Item $localDirPath -ItemType directory | Out-Null+                        // 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);
-····················DownloadDirectory $session $localDirPath $remoteFilePath+························// 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); 
 +                        } 
 + ···················}
                }                 }
            }             }
-            else 
-            { 
-                Write-Host ("Downloading file {0}..." -f $remoteFilePath) 
-                # Download file 
-                $transferResult = $session.GetFiles($remoteFilePath, ($localPath + "\")) 
-················# Did the download succeded? +············return 0; 
- ···············if (!$transferResult.IsSuccess+ ·······} 
- ···············+ ·······catch (Exception e
- ···················# Print error (but continue with other files) + ·······
-                    Write-Host ("Error downloading file {0}: {1}" -f $remoteFilePath, $transferResult.Failures[0].Message) + ···········Console.WriteLine("Error: {0}", e); 
-                } +            return 1;
-            }+
        }         }
-    } 
-    catch [Exception] 
-    { 
-        Write-Host ("Error downloading directory {0}: {1}" -f $remotePath, $_.Exception.Message) 
    }     }
} }
- +</code> 
 + 
 +==== [[powershell]] PowerShell Example ==== 
 + 
 +<code powershell> 
 +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 try
{ {
    # Load WinSCP .NET assembly     # Load WinSCP .NET assembly
    Add-Type -Path "WinSCPnet.dll"     Add-Type -Path "WinSCPnet.dll"
-·+
    # Setup session options from URL     # Setup session options from URL
    $sessionOptions = New-Object WinSCP.SessionOptions     $sessionOptions = New-Object WinSCP.SessionOptions
-    $sessionOptions.ParseUrl($sessionUrl); +    $sessionOptions.ParseUrl($sessionUrl) 
-·+
    $session = New-Object WinSCP.Session     $session = New-Object WinSCP.Session
    $session.SessionLogPath = "session.log"     $session.SessionLogPath = "session.log"
-····+
    try     try
    {     {
        # Connect         # Connect
        $session.Open($sessionOptions)         $session.Open($sessionOptions)
-········ + 
-        # Start recursion +        # Enumerate files and directories to download 
-        DownloadDirectory $session $localPath $remotePath+        $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     finally
Line 92: Line 215:
        $session.Dispose()         $session.Dispose()
    }     }
-·+
    exit 0     exit 0
} }
-catch [Exception]+catch
{ {
-    Write-Host $_.Exception.Message+    Write-Host "Error: $($_.Exception.Message)"
    exit 1     exit 1
} }
</code> </code>
 +
 +==== Upload ====
 +
 +For an example of walking a local tree to upload files individually, see [[library_example_moves_files_keeping_directory_structure|*]].
 +
 +The upload example calls ''[[library_operationresultbase#check|OperationResultBase.Check]]'', so it aborts on any error. Just replace the call with ''[[library_operationresultbase#issuccess|OperationResultBase.IsSuccess]]'' test, as the download example above does. See [[library_session#results|Capturing results of operations]].

Last modified: by martin