Introduction
There are a number of .NET projects out there that allow you to use SFTP, but all of them I've tried are confusing or just don't work. The makers of PuTTY (an open source telnet/SSH/etc client) made a command line app (PSFTP download it here) specifically for people scripting SFTP. Essentially, it's a command line app that takes a few arguements and can run a batch script. The code below is a basic introduction to using it with your C# apps. It generates the batch script automatically and runs PSFTP as a process. For more uses of PSFTP, read the documetation here.
Two catches
- You have to accept the SSH certificate from the server you're trying to connect to prior to running the script below or it WILL NOT work, to do this, just use PuTTY or some other SSH client to connect to the server and accept its certificate;
- This is ONLY for SFTP! For regular FTP, check out my other post here.
Using the Code
class Program
{
static void Main(string[] args)
{
string[] remoteFiles = new string[]
{
@"/etc/remote-file1.txt",
@"/etc/remote-file2.txt",
@"/etc/remote-file3.txt"
};
string[] localFiles = new string[]
{
@"C:\local-file1.txt",
@"C:\local-file2.txt",
@"C:\local-file3.txt"
};
string[] commands = new string[]
{
@"cd /home",
@"dir"
};
PSFTP PsftpClient = new PSFTP(@"10.10.10.10", @"root", @"password");
PsftpClient.Get(remoteFiles, localFiles);
PsftpClient.Put(remoteFiles, localFiles);
PsftpClient.SendCommands(commands);
Console.WriteLine(PsftpClient.Outputs);
PsftpClient = null;
}
}
Here is the actual code. You want to make sure you are 'using
' System.Diagnostics
.
class PSFTP
{
private string _Host;
private string _User;
private string _Password;
private string _BatchFilePath = @"C:\batch.txt";
private string _PsftpPath = @"C:\psftp";
public string Outputs = "";
public PSFTP(string Host, string User, string Password)
{
_Host = Host;
_User = User;
_Password = Password;
}
public PSFTP(string Host, string User, string Password, string[] Commands)
{
_Host = Host;
_User = User;
_Password = Password;
GenerateBatchFile(Commands);
}
public void Get(string[] Remote, string[] Local)
{
string[] Commands = new string[Remote.Count()];
for (int i = 0; i < Remote.Count(); i++)
{
Commands[i] = @"get " + Remote[i] + @" " + Local[i];
}
GenerateBatchFile(Commands);
Run();
return;
}
public void Put(string[] Remote, string[] Local)
{
string[] Commands = new string[Remote.Count()];
for (int i = 0; i < Remote.Count(); i++)
{
Commands[i] = @"put " + Remote[i] + @" " + Local[i];
}
GenerateBatchFile(Commands);
Run();
return;
}
public void SendCommands(string[] commands)
{
GenerateBatchFile(commands);
Run();
return;
}
private void GenerateBatchFile(string[] Commands)
{
try
{
StreamWriter batchWriter = new StreamWriter(_BatchFilePath);
for (int i = 0; i < Commands.Count(); i++)
{
batchWriter.WriteLine(Commands[i]);
}
batchWriter.WriteLine(@"bye");
batchWriter.Close();
}
catch (Exception ex) { Console.WriteLine(ex.ToString()); }
return;
}
private void Run()
{
try
{
ProcessStartInfo PsftpStartInfo = new ProcessStartInfo(_PsftpPath,
_User + @"@" + _Host + @" -pw " + _Password + @" -batch -be -b " + _BatchFilePath);
PsftpStartInfo.RedirectStandardInput = true;
PsftpStartInfo.RedirectStandardOutput = true;
PsftpStartInfo.RedirectStandardError = true;
PsftpStartInfo.UseShellExecute = false;
Process PsftpProcess = new Process();
PsftpProcess.StartInfo = PsftpStartInfo;
PsftpProcess.Start();
StreamReader PsftpOutput = PsftpProcess.StandardOutput;
StreamReader PsftpError = PsftpProcess.StandardError;
StreamWriter PsftpInput = PsftpProcess.StandardInput;
while (!PsftpOutput.EndOfStream)
{
try
{
Outputs += PsftpOutput.ReadLine();
Outputs += PsftpError.ReadLine();
}
catch (Exception ex) { Console.WriteLine(ex.ToString()); }
}
PsftpOutput.Close();
PsftpError.Close();
PsftpInput.Close();
PsftpProcess.WaitForExit();
PsftpStartInfo = null;
PsftpProcess = null;
}
catch (Exception ex) { Console.WriteLine(ex.ToString()); }
try
{
File.Delete(_BatchFilePath);
}
catch (Exception ex) { Console.WriteLine(ex.ToString()); }
return;
}
}
Points of Interest
There's certainly room to add more features depending on your needs. Threading this wouldn't be a difficult task.