I have read on WinSCP's site that you can create a file on the remote FTP server using the method Session.PutFile
and using a MemoryStream
to create the object on the remote directory.
The syntax displayed however seems to be missing something
https://winscp.net/eng/docs/library_session_putfile
It mentions to create the MemoryStream
and then pass an argument for the remote directory path and the filename.
I am using it within a function I created called SFTPCreatetrigger
, as the name states the point is to use this to create an equivalent trigger file with the same name on a separate directory as each file uploads to another.
However I just keep getting the error New-Object:
Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'ComObject'. Specified method is not supported.
## ---------------------------------------------------------
## Function to Create file on Remote Directory
## ---------------------------------------------------------
Function SftpCreateTriggerFile
{
param
(
[string]$FtpServer,
[string]$FtpTriggerDirectory,
[string]$FtpUsername,
[string]$FtpPassword,
[string]$FtpSshHostKeyFingerprint
)
[hashtable]$Return = @{}
## Get initial time
$dt = Get-Date
$StartTime = Get-Date $dt -f "MMddyy HH:mm:ss"
$StartTimeUtc = $dt.ToUniversalTime().ToString("MMddyy HH:mm:ss")
## Load WinSCP .NET assembly
[Reflection.Assembly]::LoadFrom("C:\Program Files (x86)\WinSCP\WinSCPnet.dll") | Out-Null
## Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Sftp
$sessionOptions.HostName = $FtpServer
$sessionOptions.UserName = $FtpUsername
$sessionOptions.Password = $FtpPassword
$sessionOptions.PortNumber = 22
$sessionOptions.SshHostKeyFingerprint = $FtpSshHostKeyFingerprint
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Automatic
$transferOptions.FilePermissions = $null
$transferOptions.PreserveTimestamp = $false
$transferOptions.ResumeSupport.State = [WinSCP.TransferResumeSupportState]::Off
$transferResult = $session.PutFile((New-Object System.IO.MemoryStream, ($FtpTriggerDirectory + $file.fullname)), $transferOptions)
# Throw on any error
$transferResult.Check()
if ($transferResult.Transfers.Count -gt 0)
{
$Return.IsSuccess = $true
$Return.StandardOutput = "Success"
}
else
{
$Return.IsSuccess = $false
$Return.StandardOutput = "Issue occurred creating trigger file on remote directory $FtpTriggerDirectory. No file created."
}
## Calculate time elapsed
$ExecTime = [math]::Round(([datetime]::Now - ([datetime]::ParseExact($StartTime,'MMddyy HH:mm:ss',$null))).TotalSeconds)
}
catch [Exception]
{
$Return.IsSuccess = $false
$Return.StandardOutput = $_.Exception.Message
## Calculate time elapsed
$ExecTime = [math]::Round(([datetime]::Now - ([datetime]::ParseExact($StartTime,'MMddyy HH:mm:ss',$null))).TotalSeconds)
}
finally
{
$Return.StartTime = $StartTime
$Return.StartTimeUtc = $StartTimeUtc
$Return.ExecTime = $ExecTime
## Disconnect and cleanup
$session.Dispose()
}
Return $Return
}
The above is the function and below is what I am trying to execute
## Set up wildcards to download files
$arrFiles = @(Get-ChildItem $LocalDirectory -Filter "*.txt")
foreach ($file in $arrFiles)
{
$arrReturnResults = SftpUploadWithWildcards -FtpServer $FtpServer -ftpdirectory $FtpDirectory -FtpUsername $FtpUsername -FtpPassword $FtpPassword -FtpSshHostKeyFingerprint $FtpSshHostKeyFingerprint -LocalDirectory $LocalDirectory -FileNameWildcards $file.Name
$ProcessName = "Upload($FtpServer):FromDir($LocalDirectory):Files("+$file.Name+")"
if ($arrReturnResults.IsSuccess)
{
$arrResults += New-Object Psobject -property @{Time = $arrReturnResults.StartTime; Task = $ProcessName; Status = "Success"; Details = ""; ExecTime = $arrReturnResults.ExecTime; TimeUtc = $arrReturnResults.StartTimeUtc}
## File Archive
# Move-Item ($LocalDirectory+$file.Name) ($LocalDirectory+"Archive") -Force -ErrorAction Stop
## Create 0 byte file with the same name to the trigger folder on remote server
$arrReturnResults = SftpCreateTriggerFile -FtpServer $FtpServer -ftpTriggerdirectory $FtpTriggerDirectory -FtpUsername $FtpUsername -FtpPassword $FtpPassword -FtpSshHostKeyFingerprint $FtpSshHostKeyFingerprint
$ProcessName = "Upload($FtpServer):FromDir($LocalDirectory):TriggerFiles("+$file.Name+")"
if ($arrReturnResults.IsSuccess)
{
$arrResults += New-Object Psobject -property @{Time = $arrReturnResults.StartTime; Task = $ProcessName; Status = "Success"; Details = ""; ExecTime = $arrReturnResults.ExecTime; TimeUtc = $arrReturnResults.StartTimeUtc}
}
else
{
$arrResults += New-Object Psobject -property @{Time = $arrReturnResults.StartTime; Task = $ProcessName; Status = "Failed"; Details = $arrReturnResults.StandardOutput; ExecTime = $arrReturnResults.ExecTime; TimeUtc = $arrReturnResults.StartTimeUtc}
}
}
else
{
$arrResults += New-Object Psobject -property @{Time = $arrReturnResults.StartTime; Task = $ProcessName; Status = "Failed"; Details = $arrReturnResults.StandardOutput; ExecTime = $arrReturnResults.ExecTime; TimeUtc = $arrReturnResults.StartTimeUtc}
}
}
All my variables ($ftpserver
, $ftpusername
, $ftptriggerdirectory
etc) are set with correct values. It just seems calling
New-Object System.IO.MemoryStream
alone works to create a new MemoryStream
object
CanRead : True
CanSeek : True
CanWrite : True
Capacity : 0
Length : 0
Position : 0
CanTimeout : False
ReadTimeout :
WriteTimeout :
However I cannot assign the name I want to it (which effectively is the last $file
to have gone through the foreach loop)
I have read around MemoryStream
use and seen that you need to reference a byte
array? However I just want it to create an object on the remote directory as stated in examples I found by Martin Prikryl himself.
If your goal was to create an empty file (from a new empty MemoryStream
), then the problem is the incorrect syntax to call Session.PutFile
with three arguments. It should be:
$session.PutFile(
(New-Object System.IO.MemoryStream), # stream
($FtpTriggerDirectory + $file.fullname), # remoteFilePath
$transferOptions) # options
(The brackets around $FtpTriggerDirectory + $file.fullname
are redundant)
Also note that Session.PutFile
does not return anything. So all your code, that assigns $transferResult
and work with it, will fail. The method throws an exception on error. If you want to log the exception/error, do it in your catch [Exception]
clause (what you should be doing anyway).