I need to connect to an SFTP server to download and upload files using C#/.NET 3.5.
Does the .NET 3.5 framework provide any built-in tools/mechanisms/libraries to connect to an SFTP server to download and upload files?
There are commercial solutions:
http://www.rebex.net/sftp.net/
http://www.enterprisedt.com/products/edtftpnetpro/overview.html
...and free:
http://www.tamirgal.com/blog/page/SharpSSH.aspx
I personally have no experience with any of them.
There's no support for SFTP in .NET framework, in any version.
The answer by Sanj shows how to use WinSCP by driving its console application. While that's indeed possible, where actually WinSCP .NET assembly that does it for a developer, giving him/her native .NET interface to the WinSCP scripting.
There's even a WinSCP NuGet package.
A trivial SFTP upload C# example:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = "example.com",
UserName = "user",
Password = "mypassword",
SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx...="
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Upload files
session.PutFiles(#"d:\toupload\*", "/home/user/").Check();
}
And the same for VB.NET:
' Setup session options
Dim sessionOptions As New SessionOptions
With sessionOptions
.Protocol = Protocol.Sftp
.HostName = "example.com"
.UserName = "user"
.Password = "mypassword"
.SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx...="
End With
Using session As New Session
' Connect
session.Open(sessionOptions)
' Upload files
session.PutFiles("d:\toupload\*", "/home/user/").Check()
End Using
There are lot of other examples in both languages (and more).
You can have WinSCP GUI generate an SFTP code template, like above, for you, including C#, VB.NET and PowerShell.
As mentioned above, the assembly is just a wrapper around WinSCP scripting, so it's not a completely native .NET code. As such it does not fit all use cases in .NET framework. It is mostly suitable for automating tasks, not for developing GUI or web applications.
For a fully native .NET SFTP library, see SSH.NET, which is strangely not mentioned in any answer yet.
(I'm the author of WinSCP)
No, .NET doesn't ship with any SFTP libraries. However, WinSCP is a free utility you can download. I've used it before to download information from a PayPal report server.
It has a nice command line setup for you to automate the download / upload of files too. So from my .NET app, you just invoke the process with the specific arguments and wait until it completes.
I prefer WinSCP. It's free and I've tried it before, it works perfectly. If you download the COM file and you could do something like below:
NOTE: I'm passing the params from ini.
const string logname = "log.xml";
string username = ini.IniReadValue("sftp", "Username");
string password = ini.IniReadValue("sftp", "Password");
string remotehost = ini.IniReadValue("sftp", "Remote Host");
string dloadpath = ini.IniReadValue("Download", "Local Path");
Process winscp = new Process();
winscp.StartInfo.FileName = #ini.IniReadValue("winscp", "compath");
winscp.StartInfo.Arguments = "/log=" + logname;
winscp.StartInfo.UseShellExecute = false;
winscp.StartInfo.RedirectStandardInput = true;
winscp.StartInfo.RedirectStandardOutput = true;
winscp.StartInfo.CreateNoWindow = true;
try
{
winscp.Start();
lblconfirm.Text = "Status: WinSCP Started Successfully";
}
catch (Exception ex)
{
writeLog("from PutSFTP: Could not run the WinSCP executable " + winscp.StartInfo.FileName + Environment.NewLine + ex.Message);
}
winscp.StandardInput.WriteLine("option batch abort");
winscp.StandardInput.WriteLine("option confirm off");
winscp.StandardInput.WriteLine("open sftp://" + username + ":" + password + "#" + remotehost);
winscp.StandardInput.WriteLine("cd " + ini.IniReadValue("Download", "Remote Path"));
winscp.StandardInput.WriteLine(#"get " + "/" + ini.IniReadValue("Download", "Remote Path") + "/*" + ini.IniReadValue("Download", "FileType") + " " + ini.IniReadValue("Download", "Local Path"));
winscp.StandardInput.Close();
string output = winscp.StandardOutput.ReadToEnd();
lblconfirm.Text = "Download Success! Check logs for moe info";
winscp.WaitForExit();
The framework does not provide SFTP support.
IIS 7.x (which comes with Windows server 2008 or Windows 7) supports only FTPs, not sFTP natively (explanation see update below) - however there are server solutions like this one available for the sFTP protocol.
If you require an sFTP client, Microsoft has not provided any support so far. But I've made very good expericence with a 3rd party .NET component named wodSFTP.NET, which is a sFTP client.
Here you can find the product & documentation: wodSFTP.NET.
Examples and online-documentation can be downloaded without any charge or registration from them.
Note that you can choose whether you buy the version without or with Sourcecode in C#. They have a SSH component as well. Examples are always available (regardless of the version you choose) in C# and VB.NET.
Example (from the website www.weonlydo.com, converted to C#):
var wodSFTP1 = new WeOnlyDo.Client.SFTP();
// Authenticate with server using hostname, login, password.
wodSFTP1.Hostname = "your_hostname";
wodSFTP1.Login = "your_login";
wodSFTP1.Password = "your_password";
wodSFTP1.Blocking = True; // Use synchronous connections
wodSFTP1.Connect();
wodSFTP1.GetFile("c:\", "/home/somepath/somefile.txt");
This example uses blocking (synchronous) mode. Note that you can also use the component asynchronously - in this case events will be fired and you can write event handlers to react on them.
Note that this example does not use a key to authenticate, but of course this is also supported (you can use key-based authentication, password-based authentication or both).
Update: FTPs and sFTP are different protocols. Please follow the links for an explanation.
Since there is no native support, uou can search in Google for other, commercial, components, just type something like 'SFTP components .NET'
Related
I have an application that needs to upload a file to my FTP server, but I noticed that anybody could decompile that app and get the FTP's user and password.
The code is this:
using (WebClient client = new WebClient())
{
client.Credentials = new NetworkCredential("ftpuser", "ftppassword");
client.UploadFile("ftp://ip/" + Computer + "_" + ExecTime + ".txt", "STOR", Application.StartupPath + #"\Stats.txt");
}
And I would like to hide those credentials from anyone. How could this be done?
It might be wiser to implement public FTP with no credentials and very limited access only to the resources you want. Obfuscation won't work since someone will de-obfuscate it anyway. Since you want to upload files from the client you may want to put some sort of controller so the upload mechanism can't be abused.
Or... simply give each user an account with limited access and problem sorted :).
I think this is more of a design problem :)
I have an FTP server with some files. I have the same files in in a local directory (in C:\).
When I'll run the program, I'd like it searches all files in the FTP server that have a last-modified timestamp later than the same file (equal name) in local directory and download all files founded.
Can someone give me a help or a tip, please? I'll appreciate all answers!
Unfortunately, there's no really reliable and efficient way to retrieve timestamps using features offered by the .NET framework, as it does not support the FTP MLSD command. The MLSD command provides listing of remote directory in a standardized machine-readable format. The command and the format is standardized by the RFC 3659.
Alternatives you can use that are supported by the .NET framework:
the ListDirectoryDetails method (the FTP LIST command) to retrieve details of all files in a directory and then you deal with FTP server specific format of the details (*nix format similar to ls *nix command is the most common, drawback is that the format may change over time, as for newer files "May 8 17:48" format is used and for older files "Oct 18 2009" format is used).
Examples:
DOS/Windows format: C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response
*nix format: Parsing FtpWebRequest ListDirectoryDetails line
the GetDateTimestamp method (an FTP MDTM command) to individually retrieve timestamps for each file. An advantage is that the response is standardized by the RFC 3659 to YYYYMMDDHHMMSS[.sss]. A disadvantage is that you have to send a separate request for each file, what can be quite inefficient.
const string uri = "ftp://example.com/remote/path/file.txt";
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(uri);
request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Console.WriteLine("{0} {1}", uri, response.LastModified);
Alternatively you can use a 3rd party FTP client implementation that supports the modern MLSD command.
For example the WinSCP .NET assembly supports that.
You can use the Session.ListDirectory or the Session.EnumerateRemoteFiles methods and read the RemoteFileInfo.LastWriteTime of the files in returned collection.
Or even easier, you can use the Session.SynchronizeDirectories to have the library automatically download (synchronize) the modified files:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Synchronize files
var localPath = #"d:\www";
var remotePath = "/home/martin/public_html";
session.SynchronizeDirectories(
SynchronizationMode.Local, localPath, remotePath, false).Check();
}
WinSCP GUI can generate a code template for you.
(I'm the author of WinSCP)
List all files : https://msdn.microsoft.com/en-us/library/ms229716(v=vs.110).aspx
Read date : https://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse.lastmodified(v=vs.110).aspx
We have a Windows 2008 R2 web server with FTP over SSL. This app uses .NET 4.5 and when I upload files, the date/time on the file changes to the current date/time on the server. Is there a way to have the uploaded file preserve the original (last modified) date?
Here is what I have:
FtpWebRequest clsRequest = (FtpWebRequest)WebRequest.Create(FTPFilePath);
clsRequest.EnableSsl = true;
clsRequest.UsePassive = true;
clsRequest.Credentials = new NetworkCredential(swwwFTPUser, swwwFTPPassword);
clsRequest.Method = WebRequestMethods.Ftp.UploadFile;
Byte[] bFile = File.ReadAllBytes(LocalFilePath);
Stream clsStream = clsRequest.GetRequestStream();
clsStream.Write(bFile, 0, bFile.Length);
clsStream.Close();
clsStream.Dispose();
clsRequest = null;
There's really no standard way to update timestamp of a remote file over an FTP protocol. That's probably why the FtpWebRequest does not support it.
There are two non-standard ways to update the timestamp. Either a non-standard MFMT command:
MFMT yyyymmddhhmmss path
or a non-standard use of (otherwise standard) MDTM command:
MDTM yyyymmddhhmmss path
But the FtpWebRequest does not allow you to send a custom command either.
See for example How to send arbitrary FTP commands in C#.
So you have to use a 3rd party FTP library.
For example WinSCP .NET assembly preserves a timestamp of an uploaded file by default.
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Upload
session.PutFiles(#"c:\toupload\file.txt*", "/home/user/").Check();
}
See a full example.
Note that WinSCP .NET assembly is not a native .NET assembly. It's rather a thin .NET wrapper around a console application.
(I'm the author of WinSCP)
I know that we can assign file attributes:-
//Change the file created time.
File.SetCreationTime(path, dtCreation);
//Change the file modified time.
File.SetLastWriteTime(path, dtModified);
If you can extract the original date before you save it to the server, then you can change file attributes....something like this:-
Sftp sftp = new Sftp();
sftp.Connect(...);
sftp.Login(...);
// upload the file
sftp.PutFile(localFile, remoteFile);
// assign creation and modification time attributes
SftpAttributes attributes = new SftpAttributes();
System.IO.FileInfo info = new System.IO.FileInfo(localFile);
attributes.Created = info.CreationTime;
attributes.Modified = info.LastWriteTime;
// set attributes of the uploaded file
sftp.SetAttributes(remoteFile, attributes);
I hope this points you in the right direction.
This is an older question, but I will add my solution here. I used an approach similar to the solution proposed by #Martin Prikryl, using the MDTM command. His answer shows the DateTime format string as yyyymmddhhmmss which is incorrect since it does not correctly handle the month and 24 hour time format. In this answer I corrected this issue and provided a full solution using C#.
I used the FluentFTP library which handles many other aspects of working with an FTP through C# very well. To set the modified time, this library does not support it but it has an Execute method. Using the FTP command MDTM yyyyMMddHHmmss /path/to/file.txt will set the modified time of the file.
NOTE: in my instance I needed to use the universal time, which may be the case for you.
The code below shows how to connect to the FTP and set the last modified time using the Execute method and sending the MDTM command.
FtpClient client = new FtpClient("ftp-address", "username", "password");
client.Connect();
FtpReply reply = client.Execute($"MDTM {DateTime.UtcNow.ToString("yyyyMMddHHmmss")} /path/to/file.txt");
While accessing my remote server, from SFTP, I am constantly getting this error
Connection has been unexpectedly closed. Server sent command exit status 0
I have filled same credentials in WinSCP, it is working fine. Where am I lacking?
Also, instead of .ppk file I am using "ssh-rsa 1024 #######################" in my keyfile column in my project's UI.
Thank You,
Pranay
For the problem above, I found that while attempting to "open the session" i.e session.Open(sessionOptions), it will through the exception as the server was not authenticating it.
As my task was to "password less winscp login", i.e i must have to provide:-
1.".ppk" file that is "puTTy private key".
2. Its "ssh key fingerprint".
After all day debugging, finally found that my version of winsscp.dll was old thus was not providing me the [metadata] inbuild properties as
1. SshPrivateKeyPath -> location of our ".ppk" file.
2. SshHostKeyFingerprint
3. passphrase -> only for one time login.
By updating the new version and above now I am able to open session, without any error.
Thank you.
I've also faced the same problem , when i first tried to execute the code mentioned at the below link.
https://winscp.net/eng/docs/library#example
Googled it a lot but i couldn't find any solution regarding the same.
Finally the following i relalized that i'm missing the portnumber in the example
Adding the port number field to my code solved the issue.
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = "11.22.33.44",
PortNumber = 2222, /* This is cause of the issue i was facing*/
UserName = "abcdef",
Password = "ghijklmnop",
SshHostKeyFingerprint = "ssh-rsa 2048 aa:bb:cc:dd:ee:ff:gg:hh:ii:jj:kk:ll:mm:nn:oo:pp"
};
If you can connect with WinSCP GUI, but not with .NET assembly, when running both in the same environment (computer), you have most probably missed some settings (like a port number or a host key fingerprint, as the other answers show).
The easiest solution is to have WinSCP GUI generate a code template for you based on its working settings.
If you are running the code on a different machine, check WinSCP FAQ Why does WinSCP not work in a new environment (operating system, machine, user account, network), when it works for me in a different environment already?
While the article is not explicitly about .NET assembly, but about WinSCP in general, it covers issues that you can face with the assembly too.
I have a .Net 3.5 service that runs on a stand-alone computer. The service queries the event logs of servers in multiple Active Directory domains. I would like to authenticate to these using managed code.
Currently, I authenticate either with p/invoke to WNetAddConnection2A or with processing out with a net use. For example, this works:
string user = "domain\\administrator";
string pass = "password";
string server = "server.domain.com";
string argument = String.Format("use \\\\{0}\\ipc$ /user:{1} {2}", server, user, pass);
System.Diagnostics.Process.Start("net.exe", argument);
System.Diagnostics.EventLog log = new EventLog("System", server);
Is there a way in the .Net 3.5 framework to authenticate and read the event log without having to proc out or call unmanaged code?
Have you seen the "System.Security.Principal.WindowsImpersonationContext"
and the demo written in MSDN.
Way back, in the .NET 1.1 days, I had to write a windows service for validation of users in 2 windows domains, used LDAP - and WindowsImpersonation as fail-back.
As I remember I got insperation form CodeProject and Google :-S
/Erling Damsgaard
DNS-IT ApS