Download files from SFTP with SSH.NET library

156,910

Solution 1

A simple working code to download a file with SSH.NET library is:

using (Stream fileStream = File.Create(@"C:\target\local\path\file.zip"))
{
    sftp.DownloadFile("/source/remote/path/file.zip", fileStream);
}

See also Downloading a directory using SSH.NET SFTP in C#.


To explain, why your code does not work:

The second parameter of SftpClient.DownloadFile is a stream to write a downloaded contents to.

You are passing in a read stream instead of a write stream. And moreover the path you are opening read stream with is a remote path, what cannot work with File class operating on local files only.

Just discard the File.OpenRead line and use a result of previous File.OpenWrite call instead (that you are not using at all now):

Stream file1 = File.OpenWrite(localFileName);

sftp.DownloadFile(file.FullName, file1);

Or even better, use File.Create to discard any previous contents that the local file may have.

I'm not sure if your localFileName is supposed to hold full path, or just file name. So you may need to add a path too, if necessary (combine localFileName with sDir?)

Solution 2

While the example works, its not the correct way to handle the streams...

You need to ensure the closing of the files/streams with the using clause.. Also, add try/catch to handle IO errors...

       public void DownloadAll()
    {
        string host = @"sftp.domain.com";
        string username = "myusername";
        string password = "mypassword";

        string remoteDirectory = "/RemotePath/";
        string localDirectory = @"C:\LocalDriveFolder\Downloaded\";

        using (var sftp = new SftpClient(host, username, password))
        {
            sftp.Connect();
            var files = sftp.ListDirectory(remoteDirectory);

            foreach (var file in files)
            {
                string remoteFileName = file.Name;
                if ((!file.Name.StartsWith(".")) && ((file.LastWriteTime.Date == DateTime.Today))

                    using (Stream file1 = File.OpenWrite(localDirectory + remoteFileName))
                    { 
                        sftp.DownloadFile(remoteDirectory + remoteFileName, file1);
                    }
            }

        }
    }

Solution 3

My version of @Merak Marey's Code. I am checking if files exist already and different download directories for .txt and other files

        static void DownloadAll()
    {
        string host = "xxx.xxx.xxx.xxx";
        string username = "@@@";
        string password = "123";string remoteDirectory = "/IN/";
        string finalDir = "";
        string localDirectory = @"C:\filesDN\";
        string localDirectoryZip = @"C:\filesDN\ZIP\";
        using (var sftp = new SftpClient(host, username, password))
        {
            Console.WriteLine("Connecting to " + host + " as " + username);
            sftp.Connect();
            Console.WriteLine("Connected!");
            var files = sftp.ListDirectory(remoteDirectory);

            foreach (var file in files)
            {

                string remoteFileName = file.Name;

                if ((!file.Name.StartsWith(".")) && ((file.LastWriteTime.Date == DateTime.Today)))
                { 

                    if (!file.Name.Contains(".TXT"))
                    {
                        finalDir = localDirectoryZip;
                    } 
                    else 
                    {
                        finalDir = localDirectory;
                    }


                    if (File.Exists(finalDir  + file.Name))
                    {
                        Console.WriteLine("File " + file.Name + " Exists");
                    }else{
                        Console.WriteLine("Downloading file: " + file.Name);
                          using (Stream file1 = File.OpenWrite(finalDir + remoteFileName))
                    {
                        sftp.DownloadFile(remoteDirectory + remoteFileName, file1);
                    }
                    }
                }
            }



            Console.ReadLine();

        }

Solution 4

This solves the problem on my end.

var files = sftp.ListDirectory(remoteVendorDirectory).Where(f => !f.IsDirectory);

foreach (var file in files)
{
    var filename = $"{LocalDirectory}/{file.Name}";
    if (!File.Exists(filename))
    {
        Console.WriteLine("Downloading  " + file.FullName);
        using (var localFile = File.OpenWrite(filename))
              sftp.DownloadFile(file.FullName, localFile);
    }
}

Solution 5

Massimiliano's solution has one problem which will lead to files not being completely downloaded. The FileStream must be closed. This is especially a problem for encrypted files. They won't completely decrypt intermittently without closing the stream.

var files = sftp.ListDirectory(remoteVendorDirectory).Where(f => !f.IsDirectory);

foreach (var file in files)
{
    var filename = $"{LocalDirectory}/{file.Name}";
    if (!File.Exists(filename))
    {
        Console.WriteLine("Downloading  " + file.FullName);
        using (var localFile = File.OpenWrite(filename))
            sftp.DownloadFile(file.FullName, localFile);
    }
}
Share:
156,910

Related videos on Youtube

user3195153
Author by

user3195153

Updated on August 18, 2021

Comments

  • user3195153
    user3195153 over 2 years
    string host = @"ftphost";
    string username = "user";
    string password = "********";
    string localFileName  = System.IO.Path.GetFileName(@"localfilename");
    string remoteDirectory = "/export/";
    using (var sftp = new SftpClient(host, username, password))
    {
        sftp.Connect();
        var files = sftp.ListDirectory(remoteDirectory);
        foreach (var file in files)
        {
            if (!file.Name.StartsWith("."))
            {
                string remoteFileName = file.Name;
                if (file.LastWriteTime.Date == DateTime.Today)
    
                Console.WriteLine(file.FullName);
    
                File.OpenWrite(localFileName);
    
                string sDir = @"localpath";
    
                Stream file1 = File.OpenRead(remoteDirectory + file.Name);
                sftp.DownloadFile(remoteDirectory, file1);
            }
        }
    }
    

    I am using SSH.NET (Renci.SshNet) library to work with an SFTP server. What I need to do is grab files from a specific folder on the SFTP server based on today's date. Then copy those files from the SFTP server to a local drive a server of mine.

    Above is the code I have but it is not working. Sometimes it says file does not exist but sometimes the files I will be downloading will not be on my local servers but I need to download whatever files were uploaded to the remote folder for that day.

    Can someone take a look and see what is wrong? I believe it has something to do with the stream portion. I have worked with FTP much besides uploading files which I took some previous code I had and thought I could reverse the process but that isn't working. The code I used is based off of this example.

  • Drag and Drop
    Drag and Drop almost 7 years
    If you want to add something based on extention use FileInfo fi, you can simply do: string ext = fi.Extension; And a switch. Because LOVE-LETTER-FOR-YOU.TXT.vbs will be a false positive. or Path.GetExtension(myFilePath);
  • Merak Marey
    Merak Marey over 6 years
    beware, you are checking if filename contains ".TXT" and depending on the Server OS you may get ".txt" (lowercase)..it will be a good idea to lowercase remoteFilename variable and use it for the conditions too instead of file.Name...
  • Gordon Bell
    Gordon Bell over 6 years
    Better: if (!file.Name.ToLower().EndsWith(".txt"))