Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Downloading a directory using SSH.NET SFTP in C#

I am using Renci.SSH and C# to connect to my Unix server from a Windows machine. My code works as expected when the directory contents are only files, but if the directory contains a folder, I get this

Renci.SshNet.Common.SshException: 'Failure'

This is my code, how can I update this to also download a directory (if exists)

private static void DownloadFile(string arc, string username, string password)
{
    string fullpath;
    string fp;
    var options = new ProgressBarOptions
    {
        ProgressCharacter = '.',
        ProgressBarOnBottom = true
    };

    using (var sftp = new SftpClient(Host, username, password))
    {
        sftp.Connect();
        fp = RemoteDir + "/" + arc;
        if (sftp.Exists(fp))     
            fullpath = fp;
        else
            fullpath = SecondaryRemoteDir + d + "/" + arc;

        if (sftp.Exists(fullpath))
        {
            var files = sftp.ListDirectory(fullpath);
            foreach (var file in files)
            {
                if (file.Name.ToLower().Substring(0, 1) != ".")
                {
                    Console.WriteLine("Downloading file from the server...");
                    Console.WriteLine();
                    using (var pbar = new ProgressBar(100, "Downloading " + file.Name + "....", options))
                    {
                        SftpFileAttributes att = sftp.GetAttributes(fullpath + "/" + file.Name);
                        var fileSize = att.Size;
                        var ms = new MemoryStream();
                        IAsyncResult asyncr = sftp.BeginDownloadFile(fullpath + "/" + file.Name, ms);
                        SftpDownloadAsyncResult sftpAsyncr = (SftpDownloadAsyncResult)asyncr;
                        int lastpct = 0;
                        while (!sftpAsyncr.IsCompleted)
                        {
                            int pct = (int)((long)sftpAsyncr.DownloadedBytes / fileSize) * 100;
                            if (pct > lastpct)
                                for (int i = 1; i < pct - lastpct; i++)
                                    pbar.Tick();
                        }
                        sftp.EndDownloadFile(asyncr);
                        Console.WriteLine("Writing File to disk...");
                        Console.WriteLine();
                        string localFilePath = "C:\" + file.Name;
                        var fs = new FileStream(localFilePath, FileMode.Create, FileAccess.Write);
                        ms.WriteTo(fs);
                        fs.Close();
                        ms.Close();
                    }
                }
            }
        }
        else
        {
            Console.WriteLine("The arc " + arc + " does not exist");
            Console.WriteLine();
            Console.WriteLine("Please press any key to close this window");
            Console.ReadKey();
        }
    }
}
like image 241
BandaidMan Avatar asked Sep 18 '18 18:09

BandaidMan


1 Answers

BeginDownloadFile downloads a file. You cannot use it to download a folder. For that you need to download contained files one by one.

The following example uses synchronous download (DownloadFile instead of BeginDownloadFile) for simplicity. After all, you are synchronously waiting for asynchronous download to complete anyway. To implement a progress bar with synchronous download, see Displaying progress of file download in a ProgressBar with SSH.NET.

public static void DownloadDirectory(
    SftpClient sftpClient, string sourceRemotePath, string destLocalPath)
{
    Directory.CreateDirectory(destLocalPath);
    IEnumerable<SftpFile> files = sftpClient.ListDirectory(sourceRemotePath);
    foreach (SftpFile file in files)
    {
        if ((file.Name != ".") && (file.Name != ".."))
        {
            string sourceFilePath = sourceRemotePath + "/" + file.Name;
            string destFilePath = Path.Combine(destLocalPath, file.Name);
            if (file.IsDirectory)
            {
                DownloadDirectory(sftpClient, sourceFilePath, destFilePath);
            }
            else
            {
                using (Stream fileStream = File.Create(destFilePath))
                {
                    sftpClient.DownloadFile(sourceFilePath, fileStream);
                }
            }
        }
    }
}
like image 108
Martin Prikryl Avatar answered Nov 17 '22 23:11

Martin Prikryl