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 source. This way the batch operation however stop 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("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/", | + | $_.Continue() |
- | ···[Parameter(Mandatory)] | + | } ) |
- | ···$remotePath, | + | |
- | ···[Parameter(Mandatory)] | + | $session.GetFiles("/home/user/*", "d:\download\").Check() |
- | ···$localPath | + | </code> |
- | ) | + | |
- | + | ==== Upload ==== | |
- | function DownloadDirectory ($session, $localPath, $remotePath) | + | |
+ | The same mechanism can be used for uploads with ''[[library_session_putfiles|Session.PutFiles]]'' or synchronization with ''[[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 "..")) | + | 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) | ||
{ | { | ||
- | # Recurse into subdirectories | + | string localFilePath = |
- | ···················$localDirPath = ($localPath + "\" + $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]]. |