Differences

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

library_example_moves_files_keeping_directory_structure 2016-03-31 library_example_moves_files_keeping_directory_structure 2023-11-15 (current)
Line 1: Line 1:
-~~NOINDEX~~ +====== Recursively move files in directory tree to/from SFTP/FTP server while preserving source directory structure ======
- +
-====== Recursively moves files in directory tree to/from SFTP/FTP server while preserving source directory structure ======+
When moving files to/from the server, WinSCP by defaults moves the subfolders too (removes them from the source directory). When moving files to/from the server, WinSCP by defaults moves the subfolders too (removes them from the source directory).
Line 7: Line 5:
===== Upload ===== ===== Upload =====
- 
-//The upload examples rely on ''Session.TranslateLocalPathToRemote'' which will be available in the upcoming WinSCP 5.8.3. You can implement your alternative instead meanwhile.// &future 
==== C# ==== ==== C# ====
-Use the ''[[https://msdn.microsoft.com/en-us/library/dd383689.aspx|DirectoryInfo.EnumerateFileSystemInfos]]'' method to walk the source local tree.+Use the ''[[dotnet>system.io.directoryinfo.enumeratefilesysteminfos|DirectoryInfo.EnumerateFileSystemInfos]]'' method to walk the source local tree.
<code csharp> <code csharp>
Line 33: Line 29:
                UserName = "user",                 UserName = "user",
                Password = "mypassword",                 Password = "mypassword",
-                SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"+                SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx..."
            };             };
Line 45: Line 41:
                // Enumerate files and directories to upload                 // Enumerate files and directories to upload
-                IEnumerable<FileSystemInfo> fileInfos = new DirectoryInfo(localPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories);+                IEnumerable<FileSystemInfo> fileInfos = 
 + ···················new DirectoryInfo(localPath).EnumerateFileSystemInfos( 
 +························"*", SearchOption.AllDirectories);
                foreach (FileSystemInfo fileInfo in fileInfos)                 foreach (FileSystemInfo fileInfo in fileInfos)
                {                 {
-                    string remoteFilePath = session.TranslateLocalPathToRemote(fileInfo.FullName, localPath, remotePath);+                    string remoteFilePath = 
 +                       RemotePath.TranslateLocalPathToRemote( 
 +····························fileInfo.FullName, localPath, remotePath); 
                    if (fileInfo.Attributes.HasFlag(FileAttributes.Directory))                     if (fileInfo.Attributes.HasFlag(FileAttributes.Directory))
                    {                     {
Line 60: Line 61:
                    else                     else
                    {                     {
-                        Console.WriteLine(string.Format("Moving file {0}...", fileInfo.FullName));+                        Console.WriteLine("Moving file {0}...", fileInfo.FullName);
                        // Upload file and remove original                         // Upload file and remove original
                        session.PutFiles(fileInfo.FullName, remoteFilePath, true).Check();                         session.PutFiles(fileInfo.FullName, remoteFilePath, true).Check();
Line 78: Line 79:
</code> </code>
 +==== [[upload_powershell]] PowerShell ====
 +
 +You can install this script as an [[extension|WinSCP extension]] by using this page URL in the //[[ui_pref_commands#extensions|Add Extension]]// command.
 +
 +<code powershell - UploadDeleteKeepStructure.ps1>
 +# @name        Upload and Delete Files
 +# @command      powershell.exe -ExecutionPolicy Bypass -File "%EXTENSION_PATH%" ^
 +#                  -sessionUrl "!E" -remotePath "!/" -sessionLogPath "%SessionLogPath%" ^
 +#                  -pause !&
 +# @description  Moves selected local files to a remote directory, ^
 +#                  but keeps local directory structure
 +# @flag        ApplyToDirectories
 +# @version      5
 +# @homepage    ~~SELF~~
 +# @require      WinSCP 5.16
 +# @option      SessionLogPath -config sessionlogfile
 +# @optionspage  ~~SELF~~#options
 +
 +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,
 +    $sessionLogPath = $Null,
 +    [Switch]
 +    $pause,
 +    [Parameter(Mandatory = $True, ValueFromRemainingArguments = $True, Position = 0)]
 +    $localPaths
 +)
 +
 +try
 +{
 +    # Load WinSCP .NET assembly
 +    $assemblyPath = if ($env:WINSCP_PATH) { $env:WINSCP_PATH } else { $PSScriptRoot }
 +    Add-Type -Path (Join-Path $assemblyPath "WinSCPnet.dll")
 +
 +    # Setup session options from URL
 +    $sessionOptions = New-Object WinSCP.SessionOptions
 +    $sessionOptions.ParseUrl($sessionUrl)
 +
 +    $session = New-Object WinSCP.Session
 +
 +    try
 +    {
 +        $session.SessionLogPath = $sessionLogPath
 +
 +        # Connect
 +        $session.Open($sessionOptions)
 +
 +        foreach ($localPath in $localPaths)
 +        {
 +            # If the selected item is file, find all contained files and folders recursively
 +            if (Test-Path $localPath -PathType container)
 +            {
 +                $files =
 +                    @($localPath) +
 +                    (Get-ChildItem $localPath -Recurse | Select-Object -ExpandProperty FullName)
 +            }
 +            else
 +            {
 +                $files = $localPath
 +            }
 +
 +            $parentLocalPath = Split-Path -Parent (Resolve-Path $localPath)
 +
 +            foreach ($localFilePath in $files)
 +            {
 +                $remoteFilePath =
 +                    [WinSCP.RemotePath]::TranslateLocalPathToRemote(
 +                        $localFilePath, $parentLocalPath, $remotePath)
 +
 +                if (Test-Path $localFilePath -PathType container)
 +                {
 +                    # Create remote subdirectory, if it does not exist yet
 +                    if (!($session.FileExists($remoteFilePath)))
 +                    {
 +                        $session.CreateDirectory($remoteFilePath)
 +                    }
 +                }
 +                else
 +                {
 +                    Write-Host "Moving file $localFilePath to $remoteFilePath..."
 +                    # Upload file and remove original
 +                    $session.PutFiles($localFilePath, $remoteFilePath, $True).Check()
 +                }
 +            }
 +        }
 +
 +        & "$env:WINSCP_PATH\WinSCP.exe" "$sessionUrl" /refresh "$remotePath"
 +    }
 +    finally
 +    {
 +        # Disconnect, clean up
 +        $session.Dispose()
 +    }
 +
 +    $result = 0
 +}
 +catch
 +{
 +    Write-Host "Error: $($_.Exception.Message)"
 +    $result = 1
 +}
 +
 +# Pause if -pause switch was used
 +if ($pause)
 +{
 +    Write-Host "Press any key to exit..."
 +    [System.Console]::ReadKey() | Out-Null
 +}
 +
 +exit $result
 +</code>
 +
 +=== [[options]] Options ===
 +
 +In the //Session log file//, you can specify a path to a [[logging|session log file]].
 +
 +In the //Keyboard shortcut//, you can specify a [[custom_key_shortcuts|keyboard shortcut]] for the extension.
 +
 +===== [[download]] Download =====
 +
 +For a download, you can use the code from the [[library_example_recursive_download_custom_error_handling#tree_download|Explicit implementation of a file tree download section of Recursively download directory tree with custom error handling]] example.
 +
 +Just pass a ''true'' to the optional ''[[library_session_getfiles#remove|remove]]'' parameter of the ''[[library_session_getfiles|Session.GetFiles]]''.
 +
 +==== C# ====
 +
 +<code csharp>
 +session.GetFiles(remoteFilePath, localFilePath, true).Check();
 +</code>
 +
 +==== PowerShell ====
 +
 +<code powershell>
 +$session.GetFiles($remoteFilePath, $localFilePath, $True).Check()
 +</code>

Last modified: by martin