Differences
This shows you the differences between the selected revisions of the page.
2014-11-24 | 2014-11-25 | ||
improving testing for errorlevel (martin) | Locking files while uploading / Upload to temporary file name (martin) | ||
Line 604: | Line 604: | ||
$transfer.FileName, $transfer.Error.Message) | $transfer.FileName, $transfer.Error.Message) | ||
} | } | ||
+ | } | ||
+ | } | ||
+ | finally | ||
+ | { | ||
+ | # Disconnect, clean up | ||
+ | $session.Dispose() | ||
+ | } | ||
+ | |||
+ | exit 0 | ||
+ | } | ||
+ | catch [Exception] | ||
+ | { | ||
+ | Write-Host $_.Exception.Message | ||
+ | exit 1 | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ===== [[locking_files_while_uploading]] Locking files while uploading / Upload to temporary file name ===== | ||
+ | |||
+ | You may have an automated system monitoring a remote folder and you want to prevent it from accidentally picking a file that has not finished uploading yet. As majority of SFTP and FTP servers do not support file locking (and neither does WinSCP), you need to prevent the automated system from picking the file otherwise. | ||
+ | |||
+ | Common workarounds are: | ||
+ | |||
+ | - Upload "done" file once an upload of data files finishes and have the automated system wait for the "done" file before processing the data files. This is easy solution, but won't work in multi-user environment. | ||
+ | - Upload data files to temporary ("upload") folder and move them atomically to target folder once the upload finishes. | ||
+ | - Upload data files to distinct temporary name, e.g. with ''.filepart'' extension, and rename them atomically once the upload finishes. Have the automated system ignore the ''.filepart'' files. | ||
+ | |||
+ | Here we focus on the third approach (although the second is very similar, implementation-wise). | ||
+ | |||
+ | ==== Using Transfer to temporary filename feature ==== | ||
+ | |||
+ | With [[sftp|SFTP protocol]], you can use [[resume#automatic|Transfer to temporary filename]] feature to have WinSCP handle the rename automatically for you. | ||
+ | |||
+ | In [[scripting]], use: | ||
+ | |||
+ | <code winscp> | ||
+ | put -resumesupport=on d:\toupload\*.txt /home/martin/upload/ | ||
+ | </code> | ||
+ | |||
+ | With [[library|WinSCP .NET assembly]], use: | ||
+ | |||
+ | <code powershell> | ||
+ | $transferOptions = New-Object WinSCP.TransferOptions | ||
+ | $transferOptions.ResumeSupport.State = [WinSCP.TransferResumeSupportState]::On | ||
+ | $session.PutFiles("d:\toupload\*.txt", "/home/martin/upload/", $False, $transferOptions).Check() | ||
+ | </code> | ||
+ | |||
+ | ==== Moving/renaming uploaded files when upload finishes ==== | ||
+ | |||
+ | If you need to use a different name pattern or a protocol different from SFTP or you want to take the second approach, you need to code the rename/move. | ||
+ | |||
+ | Following example shows implementation of the third approach using [[library|WinSCP .NET assembly]] in [[library_powershell|PowerShell]]: | ||
+ | |||
+ | <code powershell> | ||
+ | try | ||
+ | { | ||
+ | # Load WinSCP .NET assembly | ||
+ | Add-Type -Path "WinSCPnet.dll" | ||
+ | |||
+ | # Setup session options | ||
+ | $sessionOptions = New-Object WinSCP.SessionOptions | ||
+ | $sessionOptions.Protocol = [WinSCP.Protocol]::Sftp | ||
+ | $sessionOptions.HostName = "example.com" | ||
+ | $sessionOptions.UserName = "user" | ||
+ | $sessionOptions.Password = "mypassword" | ||
+ | $sessionOptions.SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx" | ||
+ | |||
+ | $session = New-Object WinSCP.Session | ||
+ | |||
+ | try | ||
+ | { | ||
+ | # Connect | ||
+ | $session.Open($sessionOptions) | ||
+ | |||
+ | $localPath = "c:\toupload\" | ||
+ | $remotePath = "/home/user/upload/" | ||
+ | # Deliberately using an underscore instead of a dot, | ||
+ | # as the dot has specific meaning in operation mask | ||
+ | $suffix = "_filepart" | ||
+ | |||
+ | $transferOptions = New-Object WinSCP.TransferOptions | ||
+ | # Particularly with SFTP protocol, prevent additional .filepart suffix | ||
+ | # from being added to uploaded files larger than 100 KB | ||
+ | $transferOptions.ResumeSupport.State = [WinSCP.TransferResumeSupportState]::Off | ||
+ | |||
+ | # Upload all .txt files with temporary "_filepart" suffix | ||
+ | $transferResult = | ||
+ | $session.PutFiles(($localPath + "*.txt"), ($remotePath + "*.*" + $suffix), | ||
+ | $False, $transferOptions) | ||
+ | |||
+ | # Throw on any error | ||
+ | $transferResult.Check() | ||
+ | |||
+ | # Rename uploaded files | ||
+ | foreach ($transfer in $transferResult.Transfers) | ||
+ | { | ||
+ | # Remove suffix | ||
+ | $finalName = $transfer.Destination.SubString(0, $transfer.Destination.Length - $suffix.Length) | ||
+ | Write-Host ("Renaming uploaded file {0} to final name {1}" -f $transfer.Destination, $finalName) | ||
+ | # Rename uploaded file to its final name | ||
+ | $session.MoveFile($transfer.Destination, $finalName) | ||
} | } | ||
} | } |