Cómo acceder a un servidor SFTP usando SSH.NET (síncrono y asíncrono) con C# en WinForms

Cómo acceder a un servidor SFTP usando SSH.NET (síncrono y asíncrono) con C# en WinForms

SFTP son las siglas de SSH File Transfer Protocol o Secure File Transfer Protocol, es un protocolo separado empaquetado con SSH que funciona de manera similar en una conexión totalmente segura. Aunque SFTP está integrado en muchas herramientas gráficas que sus usuarios podrían utilizar, si es un desarrollador, puede integrar dicha función en su aplicación. De esta forma puedes manejar lo que realmente hacen los usuarios cuando trabajan con esta herramienta y seguirán usando una única aplicación para todo, es decir, la tuya.

En este artículo, le mostraremos cómo instalar y cómo lograr algunas tareas típicas cuando trabaja con SFTP en WinForms C # utilizando la biblioteca SSH.NET ampliamente conocida.

1. Instale SSH.NET

Como primer paso, proceda a instalar la biblioteca SSH.NET en su proyecto a través de NuGET. SSH.NET es una biblioteca Secure Shell (SSH) para .NET, optimizada para el paralelismo. Este proyecto se inspiró en la biblioteca Sharp.SSH que se portó desde Java y parece que no fue compatible durante bastante tiempo. Esta biblioteca es una reescritura completa utilizando .NET 4.0, sin dependencias de terceros, utilizando el paralelismo para lograr el mejor rendimiento posible. Proporciona funcionalidad SFTP para operaciones sincrónicas y asincrónicas, eso es exactamente lo que necesitamos.

Abra su proyecto en Visual Studio y vaya al Explorador de soluciones en el área superior derecha de la ventana y haga clic derecho en la solución de su proyecto. En el menú contextual, seleccione la opción Administrar paquetes NuGet :

Install SSH.NET with nuGet

Desde la ventana emergente (o pestaña) navegue hasta la pestaña Examinar y busque SSH.NET. De la lista de resultados, seleccione la primera opción por el autor Renci y proceda a instalar:

Install SSH.NET from the nuGet repository by renci

Una vez que finalice la instalación de la biblioteca podrás utilizarla en tu proyecto sin problema. No olvide incluir el tipo SshNet en la parte superior de su clase (donde desee usarlo) y otros:

using Renci.SshNet;
using Renci.SshNet.Sftp;
using System.IO;

2. Uso

El uso se mostrará básicamente con muchos ejemplos sobre cómo puede lograr las tareas más típicas que necesita lograr con SFTP:

Lista de archivos de un directorio

Puede enumerar el contenido de un directorio utilizando el siguiente fragmento (de forma sincrónica) que utiliza el método SFTPClient.ListDirectory:

/// <summary>
/// Enumere un directorio remoto en la consola.
/// </summary>
private void listFiles()
{
    string host = @"yourSftpServer.com";
    string username = "root";
    string password = @"p4ssw0rd";

    string remoteDirectory = "/some/example/directory";

    using (SftpClient sftp = new SftpClient(host, username, password))
    {
        try
        {
            sftp.Connect();

            var files = sftp.ListDirectory(remoteDirectory);

            foreach (var file in files)
            {
                Console.WriteLine(file.Name);
            }

            sftp.Disconnect();
        }
        catch (Exception e)
        {
            Console.WriteLine("Se ha detectado una excepción " + e.ToString());
        }
    }
}

O de forma asincrónica ejecutando el código en otro hilo:

Thread myThread = new System.Threading.Thread(delegate () {
    string remoteDirectory = "/some/example/directory";

    string host = @"yourSftpServer.com";
    string username = "root";
    string password = @"p4ssw0rd";

    using (SftpClient sftp = new SftpClient(host, username, password))
    {
        try
        {
            sftp.Connect();

            var files = sftp.ListDirectory(remoteDirectory);

            foreach (var file in files)
            {
                Console.WriteLine(file.Name);
            }

            sftp.Disconnect();
        }
        catch (Exception er)
        {
            Console.WriteLine("Se ha detectado una excepción " + er.ToString());
        }
    }
});

myThread.Start();

Conexión con archivo clave y contraseña

Si usa un archivo de clave privada y simultáneamente una contraseña para conectarse a su servidor SFTP, entonces puede usar el siguiente fragmento para crear su conexión:

string host = @"yourSftpServer.com";
string username = "root";
string password = @"p4ssw0rd";

PrivateKeyFile keyFile = new PrivateKeyFile(@"path/to/OpenSsh-RSA-key.ppk");
var keyFiles = new[] { keyFile };

var methods = new List<AuthenticationMethod>();
methods.Add(new PasswordAuthenticationMethod(username, password));
methods.Add(new PrivateKeyAuthenticationMethod(username, keyFiles));

ConnectionInfo con = new ConnectionInfo(host, 22, username, methods.ToArray());
using (var client = new SftpClient(con))
{
    client.Connect();

    // ¡Haz lo que necesites con el cliente!

    client.Disconnect();
}

Descarga de un solo archivo

Para descargar un archivo, puede usar el método SFTPClient.DownloadFile y escribirlo localmente usando el método System.IO.File.OpenWrite:

/// <summary>
/// Descarga un archivo en el escritorio de forma sincrónica
/// </summary>
public void downloadFile()
{
    string host = @"yourSftpServer.com";
    string username = "root";
    string password = @"p4ssw0rd";

    // Ruta al archivo en el servidor SFTP
    string pathRemoteFile = "/var/www/vhosts/some-folder/file_server.txt";
    // Ruta donde se debe guardar el archivo una vez descargado (localmente)
    string pathLocalFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "download_sftp_file.txt");

    using (SftpClient sftp = new SftpClient(host, username, password))
    {
        try
        {
            sftp.Connect();

            Console.WriteLine("Downloading {0}", pathRemoteFile);

            using (Stream fileStream = File.OpenWrite(pathLocalFile))
            {
                sftp.DownloadFile(pathRemoteFile, fileStream);
            }

            sftp.Disconnect();
        }
        catch (Exception er)
        {
            Console.WriteLine("Se ha detectado una excepción " + er.ToString());
        }
    }
}

O puede hacer que cree de forma asincrónica otro hilo y ejecute su código dentro:

Thread myThread = new System.Threading.Thread(delegate () {
    string host = @"yourSftpServer.com";
    string username = "root";
    string password = @"p4ssw0rd";

    // Ruta al archivo en el servidor SFTP
    string pathRemoteFile = "/var/www/vhosts/some-folder/file_server.txt";
    // Ruta donde se debe guardar el archivo una vez descargado (localmente)
    string pathLocalFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "download_sftp_file.txt");

    using (SftpClient sftp = new SftpClient(host, username, password))
    {
        try
        {
            sftp.Connect();

            Console.WriteLine("Downloading {0}", pathRemoteFile);

            using (Stream fileStream = File.OpenWrite(pathLocalFile))
            {
                sftp.DownloadFile(pathRemoteFile, fileStream);
            }

            sftp.Disconnect();
        }
        catch (Exception er)
        {
            Console.WriteLine("An exception has been caught " + er.ToString());
        }
    }
});

myThread.Start();

Descarga de un directorio completo

Si necesita descargar un directorio completo (incluso subcarpetas y subarchivo), deberá crear 2 funciones y una de ellas recursiva. La primera función será DownloadFile, que te permite a través de un directorio remoto y la ruta local como argumentos descargar el archivo. La segunda función es el método DownloadDirectory, que listará todos los archivos en el directorio providen e iterará sobre ellos. Respectivamente, si el elemento es un archivo, usará el método DownloadFile para descargarlo o si es una carpeta, lo creará:

/// <summary>
/// Descarga un directorio remoto en un directorio local
/// </summary>
/// <param name="client"></param>
/// <param name="source"></param>
/// <param name="destination"></param>
private void DownloadDirectory(SftpClient client, string source, string destination, bool recursive = false)
{
    // Enumere los archivos y carpetas del directorio
    var files = client.ListDirectory(source);

    // Iterar sobre ellos
    foreach (SftpFile file in files)
    {
        // Si es un archivo, descárgalo
        if (!file.IsDirectory && !file.IsSymbolicLink)
        {
            DownloadFile(client, file, destination);
        }
        // Si es un enlace simbólico, ignórelo
        else if (file.IsSymbolicLink)
        {
            Console.WriteLine("Symbolic link ignored: {0}", file.FullName);
        }
        // Si es un directorio, créelo localmente (e ignore .. y. =)
        //. es la carpeta actual
        //.. es la carpeta que está encima de la carpeta actual: la carpeta que contiene la carpeta actual.
        else if (file.Name != "." && file.Name != "..")
        {
            var dir = Directory.CreateDirectory(Path.Combine(destination, file.Name));
            // y comience a descargar su contenido de forma recursiva :) en caso de que sea necesario
            if (recursive)
            {
                DownloadDirectory(client, file.FullName, dir.FullName);
            }
        }
    }
}

/// <summary>
/// Descarga un archivo remoto a través del cliente en un directorio local
/// </summary>
/// <param name="client"></param>
/// <param name="file"></param>
/// <param name="directory"></param>
private void DownloadFile(SftpClient client, SftpFile file, string directory)
{
    Console.WriteLine("Downloading {0}", file.FullName);

    using (Stream fileStream = File.OpenWrite(Path.Combine(directory, file.Name)))
    {
        client.DownloadFile(file.FullName, fileStream);
    }
}

A continuación, puede proceder a crear el cliente con sus credenciales e iniciar la descarga de su directorio remoto utilizando el método creado anteriormente. Tenga en cuenta que como la descarga de un directorio completo llevará tiempo, es recomendable usarlo solo de forma asincrónica (creando un hilo), sin embargo, esto es solo una recomendación y puede desenvolver el código:

Thread myThread = new System.Threading.Thread(delegate () {
    string host = @"yourSftpServer.com";
    string username = "root";
    string password = @"p4ssw0rd";

    // Ruta a la carpeta en el servidor SFTP
    string pathRemoteDirectory = "/var/www/vhosts/some-folder-to-download";
    // Ruta donde se debe guardar el archivo una vez descargado (localmente)
    string pathLocalDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "example-local-folder");

    using (SftpClient sftp = new SftpClient(host, username, password))
    {
        try
        {
            sftp.Connect();

            // De forma predeterminada, el método no descarga subdirectorios
            // por lo tanto, debe indicar que también desea su contenido
            bool recursiveDownload = true;

            // Iniciar la descarga del directorio
            DownloadDirectory(
                sftp,
                pathRemoteDirectory,
                pathLocalDirectory,
                recursiveDownload
            );
            
            sftp.Disconnect();
        }
        catch (Exception er)
        {
            Console.WriteLine("Se ha detectado una excepción" + er.ToString());
        }
    }
});

myThread.Start();

Eliminar archivo remoto

Para eliminar un archivo de un directorio remoto, use el método SFTPClient.DeleteFile:

/// <summary>
/// Eliminar un archivo remoto
/// </summary>
private void deleteFile()
{
    string host = @"yourSftpServer.com";
    string username = "root";
    string password = @"p4ssw0rd";

    // Ruta a la carpeta en el servidor SFTP
    string pathRemoteFileToDelete = "/var/www/vhosts/folder/somefile.txt";

    using (SftpClient sftp = new SftpClient(host, username, password))
    {
        try
        {
            sftp.Connect();

            // Borrar archivo
            sftp.DeleteFile(pathRemoteFileToDelete);

            sftp.Disconnect();
        }
        catch (Exception er)
        {
            Console.WriteLine("An exception has been caught " + er.ToString());
        }
    }
}

Que te diviertas ❤️!

Esto podria interesarte

Conviertete en un programador más sociable