Downloading from an SFTP site using SSH.Net

October 17, 2020

I’ve done this a few times, and have failed to document it, and so each time it’s a pain. To be clear, if you’re downloading from FTP, you should have a look here: it’s an excellent, and simple code snippet that will do the job for you.

However, this won’t work with SFTP. Having looked into this, it looks like there’s basically two options: Chilkat if you have some money to spend, and SSH.NET if you don’t. I actually implemented Chilkat before realising it was commercial - it’s a much easier experience, and it’s commercially supported. I’m not being paid by them to say this, and you can easily get by with SSH.NET (in fact, that’s the subject of this post); but there are advantages to going with a commercial option.

Using SSH.NET

The latest version of SSH was released in 2016. There does appear to be an update being worked on, but the NuGet package (at the time of writing) is from 2016:



Install-Package SSH.NET

There’s some pretty good documentation on the GitHub site, and the two links in the references offer wrapper implementations. What’s here is not really any better than what’s there, but I hadn’t seen a post with the code in (plus, I like to have these things documented in my own words).

Client

The first thing you’ll need for each call is a client; I’ve separated mine into a method:



SftpClient GetClient()
{
    var connectionInfo = new PasswordConnectionInfo(url, port, username, password);

    var client = new SftpClient(connectionInfo);
    client.Connect();
    return client;
}

If you’re not sure what your port is, it’s probably 22, although I can’t help with the rest. We’re going to cover 5 basic methods here: List, Upload, Download, Read and Delete.

List



        IEnumerable<SftpFile> ListFiles(string directory)
        {
            using var client = GetClient();
            try
            {                
                return client.ListDirectory(directory);
            }
            catch (Exception exception)
            {
                // Log error
                throw;
            }
            finally
            {
                client.Disconnect();
            }
        }

There’s not much to explain here - ListDirectory returns a list of SftpFiles. The parameter directory is the directory on the remote server; if you want to access the base directory, then directory = ”.”. It’s worth looking at the finally block, though. You should disconnect the client when you’re done.

Upload



        void UploadFile(string localPath, string remotePath)
        {
            using var client = GetClient();
            try
            {
                using var s = File.OpenRead(localPath);
                client.UploadFile(s, remotePath);
            }
            catch (Exception exception)
            {
                // Log error
                throw;
            }
            finally
            {
                client.Disconnect();
            }
        }

Again, not much here: simply creating a stream, and passing it to client.UploadFile().

Download

This is basically the reverse of UploadFile. In this case, we create the stream locally and download to it:



        void DownloadFile(string remotePath, string localPath)
        {
            using var client = GetClient();
            try
            {
                using var s = File.Create(localPath);
                client.DownloadFile(remotePath, s);
            }
            catch (Exception exception)
            {
                // Log error
                throw;
            }
            finally
            {
                client.Disconnect();
            }
        }

Read

The Read functionality is, perhaps, the most trivial, and the most useful:



        string ReadFile(string remotePath)
        {
            using var client = GetClient();
            try
            {                
                return client.ReadAllText(remotePath);
            }
            catch (Exception exception)
            {
                // Log error
                throw;
            }
            finally
            {
                client.Disconnect();
            }
        }

Depending on your use case, this might be all you need.

Delete

Finally, the Delete method:



        void DeleteFile(string remotePath)
        {
            using var client = GetClient();
            try
            {
                client.DeleteFile(remotePath);
            }
            catch (Exception exception)
            {
                // Log error
                throw;
            }
            finally
            {
                client.Disconnect();
            }
        }

Summary

You might be wondering what the purpose of these wrapper functions are: they do little more than call the underlying SSH library. The only reason I can give, other than that it provides some reusable documentation, is that one day the new version of SSH might be out (or you may choose to switch the Chilkat). Having already done the opposite, I can attest to how much easier that is, if you’re not picking through the main code trying to extract pieces of SSH.NET.

References

https://github.com/dotnet-labs/SftpService/blob/master/SFTPService/SftpService.cs

https://github.com/jorgepsmatos/SftpClientDemo/blob/master/Sftp.cs



Profile picture

A blog about one man's journey through code… and some pictures of the Peak District
Twitter

© Paul Michaels 2024