SFTP file transfers in .NET
Techniques demonstrated by this article are implemented for you in WinSCP .NET assembly. Using the assembly is recommended approach over implementing these techniques on your own.
This guide describes how to implement SFTP transfer in .NET application using WinSCP.
WinSCP is SFTP client with scripting interface that you can use to automate many operations that it supports, including file transfers, synchronization and other. So WinSCP itself is not a library (e.g. .NET assembly) that you can call directly. Though this guide shows you how to use it seamlessly from the .NET code.
Advertisement
Before Starting
Before starting you should:
Using WinSCP from .NET Code
Running WinSCP Process
To run winscp.com
use System.Diagnostics.Process
. This class allows running any executable, possibly redirecting its standard input and output to a stream accessible from .NET code. Code below expects that winscp.com
(ProcessStartInfo.FileName
) can be found in current working directory or in search path. You need to provide full path otherwise.
Advertisement
Process winscp = new Process(); winscp.StartInfo.FileName = "winscp.com"; winscp.StartInfo.UseShellExecute = false; winscp.StartInfo.CreateNoWindow = true; winscp.Start();
Feeding scripting commands using standard input
You can use standard input redirection (ProcessStartInfo.RedirectStandardInput
) to feed scripting commands, sparing necessity to assemble temporary script file.1
To feed commands to standard input use Process.StandardInput
stream:
winscp.StartInfo.RedirectStandardInput = true; ... winscp.Start(); winscp.StandardInput.WriteLine("option batch abort"); winscp.StandardInput.WriteLine("option confirm off"); winscp.StandardInput.WriteLine("open mysession"); winscp.StandardInput.WriteLine("ls"); winscp.StandardInput.WriteLine("put d:\\examplefile.txt");
Capturing outputs of WinSCP process
While you can redirect standard output of WinSCP process, it is actually not very useful, as output of WinSCP does not have any predefined form (cannot be parsed). Though it can be useful to capture it, in case you want to show it to a user in your GUI or for diagnostic purposes.
If you want to collect the output, redirect the standard output before starting WinSCP (ProcessStartInfo.RedirectStandardOutput
) and read from output stream (Process.StandardOutput
). You need to continuously collect the output while the script is running. The output stream has limited capacity. Once it gets filled, WinSCP hangs waiting for free space, never finishing. That means you cannot use Process.WaitForExit
on its own to wait for script to finish. Convenient alternative is StreamReader.ReadToEnd
:
winscp.StartInfo.RedirectStandardOutput = true; ... string output = winscp.StandardOutput.ReadToEnd();
Using log file
To capture results of script, you can use XML logging. For this you need to instruct WinSCP to store log file using /xmllog
command-line parameter (ProcessStartInfo.Arguments
).
const string logname = "log.xml"; ... winscp.StartInfo.Arguments = "/xmllog=\"" + logname + "\""; ... winscp.Start();
Advertisement
Note that before you can safely start reading and parsing the XML log file using tree-based parser (such as XmlDocument
or XPathDocument
), you need to wait for WinSCP to finish. See example below.
If you need to read the log file continuously, you need to use stream-based parser (such as XmlReader
). See example.
Following example shows how to use tree-based parsing using XPathDocument
:
XPathDocument log = new XPathDocument(logname); XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable()); ns.AddNamespace("w", "http://winscp.net/schema/session/1.0"); XPathNavigator nav = log.CreateNavigator();
In case of error you may check for message
elements to capture any associated error messages:
foreach (XPathNavigator message in nav.Select("//w:message", ns)) { Console.WriteLine(message.Value); }
In case of success, you can e.g. extract directory listing generated by ls
command inside ls
element:
XPathNodeIterator files = nav.Select("//w:file", ns); Console.WriteLine("There are {0} files and subdirectories:", files.Count); foreach (XPathNavigator file in files) { Console.WriteLine(file.SelectSingleNode("w:filename/@value", ns).Value); }
See also guide to Interpreting XML log for advanced scripting.
Waiting for script to complete
Use Process.WaitForExit
to wait for WinSCP process to finish.
If you have output stream redirected, you need to first read the output stream to the end.
A good practice is to close input stream too, if you have it redirected.
// If input stream is redirected only winscp.StandardInput.Close(); // If output stream is redirected only string output = winscp.StandardOutput.ReadToEnd(); // Wait for process to completely shut down winscp.WaitForExit();
Advertisement
Checking exit code
Once WinSCP script finishes, check exit code (Process.ExitCode
)
of the process:
if (winscp.ExitCode != 0) { // Error processing } else { // Success processing }
Full C# Example
Individual parts of this example are explained in the previous chapter.
using System; using System.Diagnostics; using System.Xml; using System.Xml.XPath; ... const string logname = "log.xml"; // Run hidden WinSCP process Process winscp = new Process(); winscp.StartInfo.FileName = "winscp.com"; winscp.StartInfo.Arguments = "/xmllog=\"" + logname + "\""; winscp.StartInfo.UseShellExecute = false; winscp.StartInfo.RedirectStandardInput = true; winscp.StartInfo.RedirectStandardOutput = true; winscp.StartInfo.CreateNoWindow = true; winscp.Start(); // Feed in the scripting commands winscp.StandardInput.WriteLine("option batch abort"); winscp.StandardInput.WriteLine("option confirm off"); winscp.StandardInput.WriteLine("open mysession"); winscp.StandardInput.WriteLine("ls"); winscp.StandardInput.WriteLine("put d:\\examplefile.txt"); winscp.StandardInput.Close(); // Collect all output (not used in this example) string output = winscp.StandardOutput.ReadToEnd(); // Wait until WinSCP finishes winscp.WaitForExit(); // Parse and interpret the XML log // (Note that in case of fatal failure the log file may not exist at all) XPathDocument log = new XPathDocument(logname); XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable()); ns.AddNamespace("w", "http://winscp.net/schema/session/1.0"); XPathNavigator nav = log.CreateNavigator(); // Success (0) or error? if (winscp.ExitCode != 0) { Console.WriteLine("Error occured"); // See if there are any messages associated with the error foreach (XPathNavigator message in nav.Select("//w:message", ns)) { Console.WriteLine(message.Value); } } else { // It can be worth looking for directory listing even in case of // error as possibly only upload may fail XPathNodeIterator files = nav.Select("//w:file", ns); Console.WriteLine("There are {0} files and subdirectories:", files.Count); foreach (XPathNavigator file in files) { Console.WriteLine(file.SelectSingleNode("w:filename/@value", ns).Value); } }
Advertisement
Full VB.NET Example
Individual parts of this example are explained in the first chapter. Note that the VB.NET example was not tested. It is based on C# example above though, which was. Feel free to fix it.
There’s more robust alternative implementation available.
Imports System Imports System.Diagnostics Imports System.Xml Imports System.Xml.XPath ... Const logname As String = "log.xml" ' Run hidden WinSCP process Dim winscp As Process = New Process() winscp.StartInfo.FileName = "winscp.com" winscp.StartInfo.Arguments = "/xmllog=" + logname winscp.StartInfo.UseShellExecute = False winscp.StartInfo.RedirectStandardInput = True winscp.StartInfo.RedirectStandardOutput = True winscp.StartInfo.CreateNoWindow = True winscp.Start() ' Feed in the scripting commands winscp.StandardInput.WriteLine("option batch abort") winscp.StandardInput.WriteLine("option confirm off") winscp.StandardInput.WriteLine("open mysession") winscp.StandardInput.WriteLine("ls") winscp.StandardInput.WriteLine("put d:\examplefile.txt") winscp.StandardInput.Close() ' Collect all output (not used in this example) Dim output As String = winscp.StandardOutput.ReadToEnd() ' Wait until WinSCP finishes winscp.WaitForExit() ' Parse and interpret the XML log ' (Note that in case of fatal failure the log file may not exist at all) Dim log As XPathDocument = New XPathDocument(logname) Dim ns As XmlNamespaceManager = New XmlNamespaceManager(New NameTable()) ns.AddNamespace("w", "http://winscp.net/schema/session/1.0") Dim nav As XPathNavigator = log.CreateNavigator() ' Success (0) or error? If winscp.ExitCode <> 0 Then Console.WriteLine("Error occured") ' See if there are any messages associated with the error For Each message As XPathNavigator In nav.Select("//w:message", ns) Console.WriteLine(message.Value) Next Else ' It can be worth looking for directory listing even in case of ' error as possibly only upload may fail Dim files As XPathNodeIterator = nav.Select("//w:file", ns) Console.WriteLine("There are {0} files and subdirectories:", files.Count) For Each file As XPathNavigator In files Console.WriteLine(file.SelectSingleNode("w:filename/@value", ns).Value) Next End If
Advertisement
- Of course unless what you plan to do is actually execution of existing script file.Back