This is an old revision of the document!

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 (WebDAV being an exception) do not support file locking, you need to prevent the automated system from picking the file otherwise.

Common workarounds are:

  1. 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.
  2. Upload data files to temporary (“upload”) folder and move them atomically to target folder once the upload finishes.
  3. 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.

Advertisement

Here we focus on the third approach (although the second is very similar, implementation-wise).

Using Transfer to temporary filename feature

With SFTP protocol, you can use Transfer to temporary filename feature to have WinSCP handle the rename automatically for you.

In scripting, use:

put -resumesupport=on d:\toupload\*.txt /home/martin/upload/

With WinSCP .NET assembly, use:

$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.ResumeSupport.State = [WinSCP.TransferResumeSupportState]::On
$session.PutFiles("d:\toupload\*.txt", "/home/martin/upload/", $False, $transferOptions).Check()

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 WinSCP .NET assembly in PowerShell:

param (
    $localPath = "c:\toupload\",
    $remotePath = "/home/user/upload/"
)
 
try
{
    # Load WinSCP .NET assembly
    Add-Type -Path "WinSCPnet.dll"
 
    # Setup session options
    $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
        Protocol = [WinSCP.Protocol]::Sftp
        HostName = "example.com"
        UserName = "user"
        Password = "mypassword"
        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)
 
        # 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)
        }
    }
    finally
    {
        # Disconnect, clean up
        $session.Dispose()
    }
 
    exit 0
}
catch [Exception]
{
    Write-Host $_.Exception.Message
    exit 1
}

Advertisement

Further Reading

Last modified: by martin