André Alves de Lima

Talking about Software Development and more…

Fazendo o download e upload de arquivos em um servidor FTP com C# e VB.NET

O FTP é um protocolo muito antigo, porém muito utilizado até os dias de hoje. Em algumas aplicações, pode ser pode ser que precisemos acessar um servidor FTP para baixarmos um arquivo ou até mesmo para fazermos um upload de algum arquivo relacionado ao nosso aplicativo.

No .NET Framework temos à nossa disposição a classe FtpWebRequest, que implementa todas as funcionalidades necessárias para manipularmos servidores FTP com C# e VB.NET. Porém, se quisermos fazer algo que vá além do upload e download de arquivos, o código acaba ficando um tanto quanto complexo.

Para substituir o FtpWebRequest, podemos utilizar uma infinidade de bibliotecas que implementam as funcionalidades de acesso a servidores FTP no .NET. Uma dessas bibliotecas é a FluentFTP, implementada 100% com código gerenciado e muito simples de ser utilizada.

No artigo de hoje, eu vou mostrar para você como utilizar a classe FtpWebRequest para fazer o upload e download de arquivos, bem como a biblioteca FluentFTP para outras atividades mais complexas, como criação, deleção e listagem de diretórios.

Preparando o ambiente de testes

É obvio que, para testarmos as funcionalidades deste artigo, precisaremos ter acesso a um servidor FTP. Eu poderia utilizar alguns servidores FTP que eu tenho disponíveis (como o servidor onde eu hospedo este site ou o servidor da empresa), porém ambos não permitem acesso anônimo e eu não queria correr o risco de acidentalmente compartilhar o meu usuário e senha no meio dos códigos de exemplo. Por isso, resolvi configurar um outro servidor FTP local com suporte a acesso anônimo.

Não sei se você sabe (eu não sabia), mas nós podemos configurar um servidor FTP com as próprias funcionalidades do Windows. Para conseguirmos fazer isso, temos que habilitar o IIS e criarmos um novo “FTP Site“. Eu segui as instruções deste tutorial e configurei um servidor FTP local com suporte a acesso anônimo:

How to set up and manage an FTP server on Windows 10

Se mesmo assim você achar esse processo muito complicado, saiba que existem outras opções mais simples ainda. Nesta thread do SuperUser (StackOverflow de infra) o pessoal da comunidade discutiu sobre as principais opções para configurarmos um servidor FTP no Windows com o mínimo esforço possível:

Dead-simple FTP server for Windows?

Caso você já tenha um servidor FTP acessível para fazer os testes, você obviamente não precisa seguir os passos apresentados nos links acima.

O que o FtpWebRequest tem a nos oferecer?

Uma vez tendo um servidor FTP para testarmos as funcionalidades, vamos começar a criar o nosso projeto de testes. Para simplificar as coisas, vamos criar um projeto do tipo “Console Application“. A ideia dos testes é fazermos as principais operações no nosso servidor FTP: upload, download, deleção de arquivos, criação de diretórios, deleção de diretórios e listagem dos arquivos.

O próprio .NET Framework conta com uma classe que implementa a manipulação de servidores FTP. Essa classe se chama FtpWebRequest, localizada dentro do namespace System.Net. Existem dois exemplos na própria documentação do MSDN mostrando como fazer upload e download de arquivos com o FtpWebRequest. Resolvi coloca-los à prova para ver se eles realmente funcionam (e também para convertê-los para VB.NET, uma vez que eles só estão disponíveis em C# na documentação).

Para testarmos o upload de arquivo, precisamos de um arquivo de teste. Na maioria dos exemplos que você encontra por aí, os autores trabalham com arquivos texto. Eu resolvi ser diferente e vou testar a funcionalidade de upload e download utilizando um arquivo PDF, mais especificamente este aqui, que é um guia de licenciamento do Visual Studio 2015, feito pela própria Microsoft. Se o link do Microsoft Download Center não estiver funcionando, você pode baixar o PDF diretamente aqui no meu site. Coloque o arquivo PDF dentro da pasta bin/debug do projeto.

Seguindo o exemplo de upload de arquivos da documentação no MSDN, o código ficaria assim:

        // C#
        private static void Upload(string arquivo, string destino)
        {
            var request = (System.Net.FtpWebRequest)System.Net.WebRequest.Create("ftp://localhost/" + destino);
            request.Method = System.Net.WebRequestMethods.Ftp.UploadFile;
            request.Credentials = new System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br");

            using (var stream = new System.IO.StreamReader(arquivo))
            {
                var conteudoArquivo = Encoding.UTF8.GetBytes(stream.ReadToEnd());
                request.ContentLength = conteudoArquivo.Length;

                var requestStream = request.GetRequestStream();
                requestStream.Write(conteudoArquivo, 0, conteudoArquivo.Length);
                requestStream.Close();
            }

            var response = (System.Net.FtpWebResponse)request.GetResponse();
            Console.WriteLine("Upload completo. Status: {0}", response.StatusDescription);
            response.Close();
        }
    ' VB.NET
    Private Sub Upload(Arquivo As String, Destino As String)
        Dim Request = DirectCast(System.Net.WebRequest.Create(Convert.ToString("ftp://localhost/") & Destino), System.Net.FtpWebRequest)
        Request.Method = System.Net.WebRequestMethods.Ftp.UploadFile
        Request.Credentials = New System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br")

        Using Stream = New System.IO.StreamReader(Arquivo)
            Dim ConteudoArquivo = Text.Encoding.UTF8.GetBytes(Stream.ReadToEnd())
            Request.ContentLength = ConteudoArquivo.Length

            Dim RequestStream = Request.GetRequestStream()
            RequestStream.Write(ConteudoArquivo, 0, ConteudoArquivo.Length)
            RequestStream.Close()
        End Using

        Dim Response = DirectCast(Request.GetResponse(), System.Net.FtpWebResponse)
        Console.WriteLine("Upload completo. Status: {0}", Response.StatusDescription)
        Response.Close()
    End Sub

Veja que o código não é tão simples assim. A classe FtpWebRequest, como o próprio nome já diz, trabalha com Requests e Responses. Dessa forma, para fazermos o upload de um arquivo para o servidor FTP, temos que abrir um Request no endereço desejado e temos que escrever o conteúdo do arquivo em bytes na RequestStream. A chamada no método “main” ficaria assim:

        // C#
        static void Main(string[] args)
        {
            Upload("Visual Studio 2015 Licensing Whitepaper - November-2016.pdf", "arquivo.pdf");
            Console.ReadLine();
        }
    ' VB.NET
    Sub Main()
        Upload("Visual Studio 2015 Licensing Whitepaper - November-2016.pdf", "arquivo.pdf")
        Console.ReadLine()
    End Sub

Execute a aplicação e veja o resultado retornado pelo método:

Mas, será que o upload foi realmente feito com sucesso? Ao navegar até a pasta raiz do meu servidor FTP, o arquivo realmente estava lá:

Porém, ao abrir o arquivo, o seu conteúdo estava em branco:

Pesquisando um pouco sobre esse efeito, acabei encontrando esta thread no StackOverflow, que fala justamente sobre esse problema. Aquele código disponibilizado na documentação do MSDN só funciona corretamente para arquivos texto, portanto, temos que ajustá-lo para que ele funcione para qualquer tipo de arquivo. A diferença não é tão grande assim. Basicamente, a única coisa que precisamos alterar é o jeito que lemos o arquivo de origem. Ao invés de lermos o arquivo utilizando um StreamReader, nós temos que extrair diretamente os bytes do arquivo com o método ReadAllBytes:

        // C#
        private static void Upload(string arquivo, string destino)
        {
            var request = (System.Net.FtpWebRequest)System.Net.WebRequest.Create("ftp://localhost/" + destino);
            request.Method = System.Net.WebRequestMethods.Ftp.UploadFile;
            request.Credentials = new System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br");

            var conteudoArquivo = System.IO.File.ReadAllBytes(arquivo);
            request.ContentLength = conteudoArquivo.Length;

            var requestStream = request.GetRequestStream();
            requestStream.Write(conteudoArquivo, 0, conteudoArquivo.Length);
            requestStream.Close();

            var response = (System.Net.FtpWebResponse)request.GetResponse();
            Console.WriteLine("Upload completo. Status: {0}", response.StatusDescription);
            response.Close();
        }
    ' VB.NET
    Private Sub Upload(Arquivo As String, Destino As String)
        Dim Request = DirectCast(System.Net.WebRequest.Create(Convert.ToString("ftp://localhost/") & Destino), System.Net.FtpWebRequest)
        Request.Method = System.Net.WebRequestMethods.Ftp.UploadFile
        Request.Credentials = New System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br")

        Dim ConteudoArquivo = System.IO.File.ReadAllBytes(Arquivo)
        Request.ContentLength = ConteudoArquivo.Length

        Dim RequestStream = Request.GetRequestStream()
        RequestStream.Write(ConteudoArquivo, 0, ConteudoArquivo.Length)
        RequestStream.Close()

        Dim Response = DirectCast(Request.GetResponse(), System.Net.FtpWebResponse)
        Console.WriteLine("Upload completo. Status: {0}", Response.StatusDescription)
        Response.Close()
    End Sub

Agora sim. Se executarmos novamente o projeto, veremos que o PDF será gerado corretamente dentro do servidor FTP:

OK, conseguimos implementar o upload de arquivo com sucesso utilizando o FtpWebRequest. Agora vamos testar o download utilizando o código da documentação do MSDN, com a única diferença que, ao invés de mostrarmos o conteúdo do arquivo no console, nós salvaremos o arquivo com o mesmo nome no diretório da aplicação:

        // C#
        private static void Download(string caminho)
        {
            var request = (System.Net.FtpWebRequest)System.Net.WebRequest.Create("ftp://localhost/" + caminho);
            request.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;

            request.Credentials = new System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br");
            var response = (System.Net.FtpWebResponse)request.GetResponse();

            var responseStream = response.GetResponseStream();
            using (var memoryStream = new System.IO.MemoryStream())
            {
                responseStream.CopyTo(memoryStream);
                var conteudoArquivo = memoryStream.ToArray();
                System.IO.File.WriteAllBytes(caminho, conteudoArquivo);
            }

            Console.WriteLine("Download Complete, status {0}", response.StatusDescription);
            response.Close();
        }
    ' VB.NET
    Private Sub Download(Caminho As String)
        Dim Request = DirectCast(System.Net.WebRequest.Create(Convert.ToString("ftp://localhost/") & Caminho), System.Net.FtpWebRequest)
        Request.Method = System.Net.WebRequestMethods.Ftp.DownloadFile

        Request.Credentials = New System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br")
        Dim Response = DirectCast(Request.GetResponse(), System.Net.FtpWebResponse)

        Dim ResponseStream = Response.GetResponseStream()
        Using MemoryStream = New System.IO.MemoryStream()
            ResponseStream.CopyTo(MemoryStream)
            Dim ConteudoArquivo = MemoryStream.ToArray()
            System.IO.File.WriteAllBytes(Caminho, ConteudoArquivo)
        End Using

        Console.WriteLine("Download Complete, status {0}", Response.StatusDescription)
        Response.Close()
    End Sub

Não podemos esquecer de adicionar a chamada no método “main“:

        // C#
        static void Main(string[] args)
        {
            Upload("Visual Studio 2015 Licensing Whitepaper - November-2016.pdf", "arquivo.pdf");
            Console.ReadLine();
            Download("arquivo.pdf");
            Console.ReadLine();
        }
    ' VB.NET
    Sub Main()
        Upload("Visual Studio 2015 Licensing Whitepaper - November-2016.pdf", "arquivo.pdf")
        Console.ReadLine()
        Download("arquivo.pdf")
        Console.ReadLine()
    End Sub

Execute o projeto e veja que o arquivo será baixado do servidor FTP com sucesso:

Desafios do FtpWebRequest

O upload e download de arquivos são as operações mais básicas que podemos realizar em um servidor FTP. Muitas vezes, somente essas duas operações já resolvem as necessidades do nosso projeto. Porém, existem outras operações que são tão importantes quanto o upload e download, como criação de diretórios, deleção de arquivos, listagem do conteúdo de uma pasta, etc. Será que essas outras operações também podem ser implementadas com o FtpWebRequest?

Teoricamente, sim – essas outras operações podem ser implementadas com o FtpWebRequest. Porém, o código acaba ficando um tanto quanto complexo. Veja só esta thread no StackOverflow sobre a criação de diretórios com o FtpWebRequest. Ou esta outra thread sobre a deleção de arquivos. Como você pode perceber, o código fica complicado de entender. Por isso, na minha opinião, se a sua aplicação precisa fazer mais do que um simples upload ou download de arquivos de um servidor FTP, vale a pena investigar algumas alternativas à classe FtpWebRequest.

Procurando por alternativas ao FtpWebRequest, acabei encontrando esta outra thread no StackOverflow. Várias sugestões são dadas naquela discussão, porém a que mais se destacou foi a biblioteca FluentFTP.

Conhecendo a biblioteca FluentFTP

A biblioteca FluentFTP (conhecida anteriormente como System.Net.FtpClient) é uma biblioteca desenvolvida 100% com código gerenciado e implementa todas as funcionalidades que você possa imaginar relacionadas ao acesso a servidores FTP. Ela é open source (o seu código está publicado no GitHub) e é disponibilizada através da licença MIT (extremamente permissiva).

Para instalarmos o FluentFTP no nosso projeto, basta utilizarmos o NuGet (procurando por “FluentFTP” ou digitando o comando “Install-Package FluentFTP” no Package Manager Console – caso tenha dúvidas sobre a utilização do NuGet no Visual Studio, confira este artigo):

Uma vez instalada a biblioteca, vamos ver como podemos fazer para implementarmos o upload de um arquivo.

As classes do FluentFTP ficam dentro do namespace “FluentFTP” e a principal classe que vamos utilizar é a FtpClient. Com ela nós conseguiremos fazer todas as operações no nosso servidor FTP. O upload de arquivo, por exemplo, é feito através do método OpenWrite, que cria uma Stream onde teremos que escrever o conteúdo do arquivo (como fizemos com o FtpWebRequest que vimos anteriormente).

Tomando como base o exemplo disponibilizado no GitHub da biblioteca, esta foi a minha primeira tentativa de implementação do upload de um arquivo:

        // C#
        private static void UploadFluent(string arquivo, string destino)
        {
            using (var client = new FluentFTP.FtpClient())
            {
                client.Host = "ftp://localhost";
                using (var stream = client.OpenWrite(destino))
                {
                    var conteudoArquivo = System.IO.File.ReadAllBytes(arquivo);
                    stream.Write(conteudoArquivo, 0, conteudoArquivo.Length);
                }
            }
        }
    ' VB.NET
    Private Sub UploadFluent(Arquivo As String, Destino As String)
        Using Client = New FluentFTP.FtpClient()
            Client.Host = "ftp://localhost"
            Using Stream = Client.OpenWrite(destino)
                Dim ConteudoArquivo = System.IO.File.ReadAllBytes(Arquivo)
                Stream.Write(ConteudoArquivo, 0, ConteudoArquivo.Length)
            End Using
        End Using
    End Sub

E, obviamente, temos que adicionar a chamada no método “main“:

        // C#
        static void Main(string[] args)
        {
            UploadFluent("Visual Studio 2015 Licensing Whitepaper - November-2016.pdf", "arquivo.pdf");
            Console.WriteLine("Upload FluentFTP completo");
            Console.ReadLine();
        }
    ' VB.NET
    Sub Main()
        UploadFluent("Visual Studio 2015 Licensing Whitepaper - November-2016.pdf", "arquivo.pdf")
        Console.WriteLine("Upload FluentFTP completo")
        Console.ReadLine()
    End Sub

Porém, ao testar esse código, acabei recebendo este erro:

Isso acontece porque a biblioteca FluentFTP espera que o endereço do servidor FTP não tenha o prefixo “ftp://“. Ou seja, no meu caso que o servidor está localizado no “localhost“, eu deveria passar somente “localhost“, e não “ftp://localhost“:

        // C#
        private static void UploadFluent(string arquivo, string destino)
        {
            using (var client = new FluentFTP.FtpClient())
            {
                client.Host = "localhost";
                using (var stream = client.OpenWrite(destino))
                {
                    var conteudoArquivo = System.IO.File.ReadAllBytes(arquivo);
                    stream.Write(conteudoArquivo, 0, conteudoArquivo.Length);
                }
            }
        }
    ' VB.NET
    Private Sub UploadFluent(Arquivo As String, Destino As String)
        Using Client = New FluentFTP.FtpClient()
            Client.Host = "localhost"
            Using Stream = Client.OpenWrite(destino)
                Dim ConteudoArquivo = System.IO.File.ReadAllBytes(Arquivo)
                Stream.Write(ConteudoArquivo, 0, ConteudoArquivo.Length)
            End Using
        End Using
    End Sub

Depois dessa alteração, a mensagem de erro mudou:

Dessa vez o problema é que não passamos as credenciais para o acesso ao servidor FTP. Eu achei que, devido ao servidor suportar conexões anônimas, eu não precisaria passar as credenciais para o cliente. Mas, se repararmos bem, com o FtpWebRequest nós também temos que passar as credenciais, senão o código não funciona.

Com o FluentFTP, nós passamos as credenciais para o FtpClient através da propriedade Credentials:

        // C#
        private static void UploadFluent(string arquivo, string destino)
        {
            using (var client = new FluentFTP.FtpClient())
            {
                client.Host = "localhost";
                client.Credentials = new System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br");
                using (var stream = client.OpenWrite(destino))
                {
                    var conteudoArquivo = System.IO.File.ReadAllBytes(arquivo);
                    stream.Write(conteudoArquivo, 0, conteudoArquivo.Length);
                }
            }
        }
    ' VB.NET
    Private Sub UploadFluent(Arquivo As String, Destino As String)
        Using Client = New FluentFTP.FtpClient()
            Client.Host = "localhost"
            Client.Credentials = New System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br")
            Using Stream = Client.OpenWrite(destino)
                Dim ConteudoArquivo = System.IO.File.ReadAllBytes(Arquivo)
                Stream.Write(ConteudoArquivo, 0, ConteudoArquivo.Length)
            End Using
        End Using
    End Sub

Agora sim, ao executarmos o projeto novamente, o upload do arquivo será realizado com sucesso. A implementação da funcionalidade de download é extremamente parecida. A única diferença é que, ao invés de utilizarmos o método OpenWrite, nós utilizamos o método OpenRead e gravamos o conteúdo no disco através de um FileStream:

        // C#
        private static void DownloadFluent(string caminho)
        {
            using (var client = new FluentFTP.FtpClient())
            {
                client.Host = "localhost";
                client.Credentials = new System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br");
                using (var stream = client.OpenRead(caminho))
                {
                    using (var fileStream = new System.IO.FileStream(caminho, System.IO.FileMode.Create))
                    {
                        stream.CopyTo(fileStream);
                    }
                }
            }
        }
    ' VB.NET
    Private Sub DownloadFluent(Caminho As String)
        Using Client = New FluentFTP.FtpClient()
            Client.Host = "localhost"
            Client.Credentials = New System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br")
            Using Stream = Client.OpenRead(Caminho)
                Using FileStream = New System.IO.FileStream(Caminho, System.IO.FileMode.Create)
                    Stream.CopyTo(FileStream)
                End Using
            End Using
        End Using
    End Sub

Não podemos esquecer de adicionar a chamada no método “main“!

        // C#
        static void Main(string[] args)
        {
            UploadFluent("Visual Studio 2015 Licensing Whitepaper - November-2016.pdf", "arquivo.pdf");
            Console.WriteLine("Upload FluentFTP completo");
            Console.ReadLine();
            DownloadFluent("arquivo.pdf");
            Console.WriteLine("Download FluentFTP completo");
            Console.ReadLine();
        }
    ' VB.NET
    Sub Main()
        UploadFluent("Visual Studio 2015 Licensing Whitepaper - November-2016.pdf", "arquivo.pdf")
        Console.WriteLine("Upload FluentFTP completo")
        Console.ReadLine()
        DownloadFluent("arquivo.pdf")
        Console.WriteLine("Download FluentFTP completo")
        Console.ReadLine()
    End Sub

Pronto! Com isso nós temos as funcionalidades de upload e download de arquivos implementada com a biblioteca FluentFTP. Vamos agora partir para algumas funcionalidades mais interessantes?

Deletando um arquivo do servidor

Como mencionei anteriormente, para deletarmos um arquivo do servidor FTP com o FtpWebRequest, temos que fazer um certo malabarismo. Já com a biblioteca FluentFTP, essa funcionalidade pode ser implementada de maneira extremamente simples. Basta chamarmos o método “DeleteFile” da classe FtpClient passando o caminho do arquivo que queremos deletar:

        // C#
        private static void DeletarArquivoFluent(string caminho)
        {
            using (var client = new FluentFTP.FtpClient())
            {
                client.Host = "localhost";
                client.Credentials = new System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br");
                client.DeleteFile(caminho);
            }
        }
    ' VB.NET
    Private Sub DeletarArquivoFluent(Caminho As String)
        Using Client = New FluentFTP.FtpClient()
            Client.Host = "localhost"
            Client.Credentials = New System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br")
            Client.DeleteFile(Caminho)
        End Using
    End Sub

Criando e deletando diretórios

A criação e deleção de diretórios também pode ser implementada de maneira muito simples através da biblioteca FluentFTP. Basta utilizarmos os métodos CreateDirectory e DeleteDirectory:

        // C#
        private static void CriarDiretorioFluent(string diretorio)
        {
            using (var client = new FluentFTP.FtpClient())
            {
                client.Host = "localhost";
                client.Credentials = new System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br");
                client.CreateDirectory(diretorio);
            }
        }

        private static void DeletarDiretorioFluent(string diretorio)
        {
            using (var client = new FluentFTP.FtpClient())
            {
                client.Host = "localhost";
                client.Credentials = new System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br");
                client.DeleteDirectory(diretorio, true, false);
            }
        }
    ' VB.NET
    Private Sub CriarDiretorioFluent(Diretorio As String)
        Using client = New FluentFTP.FtpClient()
            client.Host = "localhost"
            client.Credentials = New System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br")
            client.CreateDirectory(diretorio)
        End Using
    End Sub

    Private Sub DeletarDiretorioFluent(Diretorio As String)
        Using Client = New FluentFTP.FtpClient()
            Client.Host = "localhost"
            Client.Credentials = New System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br")
            Client.DeleteDirectory(Diretorio, True, False)
        End Using
    End Sub

Note que temos a opção de fazermos a deleção recursiva na chamada do método DeleteDirectory. Para isso, nós temos que passar “true” para o parâmetro “force” (que é o segundo parâmetro desse método).

Listando o conteúdo de um diretório

Por fim, vamos ver uma última funcionalidade bem interessante da biblioteca FluentFTP: a listagem dos arquivos de um diretório. O método “GetListing” da classe FtpClient implementa essa funcionalidade na biblioteca FluentFTP. Porém, esse método não faz a listagem recursiva do diretório, ou seja, se quisermos listar recursivamente todos os arquivos de um diretório, nós teremos que implementar a recursividade no lado do nosso aplicativo.

Não se assuste. Isso não é nem um pouco complicado de ser feito. Basta criarmos um método que chama ele mesmo caso o item do FTP seja do tipo “Directory“. Veja só como fica o código:

        // C#
        private static void ListarConteudoFluent()
        {
            using (var client = new FluentFTP.FtpClient())
            {
                client.Host = "localhost";
                client.Credentials = new System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br");
                ListarConteudoFluentRecursivo(client, string.Empty);
            }
        }

        private static void ListarConteudoFluentRecursivo(FluentFTP.FtpClient client, string caminho)
        {
            var arquivos = client.GetListing(caminho);
            foreach (var arquivo in arquivos)
            {
                if (arquivo.Type == FluentFTP.FtpFileSystemObjectType.Directory)
                {
                    ListarConteudoFluentRecursivo(client, arquivo.FullName);
                }
                else
                {
                    Console.WriteLine("{0} || {1} b || {2:G}", arquivo.FullName, arquivo.Size, arquivo.Created != DateTime.MinValue ? arquivo.Created : arquivo.Modified);
                }
            }
        }
    ' VB.NET
    Private Sub ListarConteudoFluent()
        Using Client = New FluentFTP.FtpClient()
            Client.Host = "localhost"
            Client.Credentials = New System.Net.NetworkCredential("anonymous", "contato@andrealveslima.com.br")
            ListarConteudoFluentRecursivo(Client, String.Empty)
        End Using
    End Sub

    Private Sub ListarConteudoFluentRecursivo(Client As FluentFTP.FtpClient, Caminho As String)
        Dim Arquivos = Client.GetListing(Caminho)
        For Each Arquivo In Arquivos
            If Arquivo.Type = FluentFTP.FtpFileSystemObjectType.Directory Then
                ListarConteudoFluentRecursivo(Client, Arquivo.FullName)
            Else
                Console.WriteLine("{0} || {1} b || {2:G}", Arquivo.FullName, Arquivo.Size, If(Arquivo.Created <> DateTime.MinValue, Arquivo.Created, Arquivo.Modified))
            End If
        Next
    End Sub

Veja que, na hora de acessarmos a data do arquivo, temos duas propriedades à nossa disposição: “Created” e “Modified“. Porém, nem todos os servidores FTP suportam a data de criação dos arquivos. Portanto, ao invés de simplesmente acessarmos diretamente a propriedade “Created“, nós verificamos se ela não é igual a DateTime.MinValue. Caso ela seja igual a DateTime.MinValue, isso significa que o servidor não informa a data de criação do arquivo, então, nós utilizamos a propriedade “Modified“, que retorna a data da última modificação do arquivo.

Eu copiei a pasta “Debug” do projeto para dentro do meu servidor FTP e o resultado da listagem dos arquivos foi este:

Concluindo

O .NET Framework conta com uma classe que implementa as funcionalidades necessárias para acessarmos um servidor FTP através dos nossos aplicativos. Essa classe, chamada FtpWebRequest, é muito simples de ser utilizada para fazermos upload e download de arquivos em um servidor FTP com C# e VB.NET. Porém, para operações mais complexas, o código acaba ficando bem difícil de entender.

Uma ótima alternativa à classe FtpWebRequest é a biblioteca FluentFTP. Essa biblioteca possui métodos muito simples para fazermos upload, download e deleção de arquivos, criação, deleção e listagem de diretórios, entre outras funcionalidades.

No artigo de hoje você conferiu como utilizar a classe FtpWebRequest para fazer o upload e download de arquivos, bem como a biblioteca FluentFTP para implementar as outras funcionalidades mais complexas.

Você já precisou trabalhar com FTP nas suas aplicações? Como é que você acabou implementando essas funcionalidades? Utilizou o FtpWebRequest ou o FluentFTP? Ou talvez até mesmo uma outra biblioteca diferente? Conte-nos mais detalhes na caixa de comentários!

Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado e ficará sabendo também em primeira mão sobre o artigo da próxima semana, além de receber dicas “bônus” que eu só compartilho por e-mail. Inscreva-se utilizando o formulário logo abaixo.

Até a próxima!

André Lima

Image by Pixabay used under Creative Commons
https://pixabay.com/en/computer-file-network-server-156949/

Newsletter do André Lima

* indicates required



Powered by MailChimp

38 thoughts on “Fazendo o download e upload de arquivos em um servidor FTP com C# e VB.NET

  • Olá André. Tenho 2 perguntas, primeira teria como eu verificar antes se há conectividade com a internet antes de fazer a operação? Segunda teria a possibilidade de no C# eu deixar programado uma espécie de despertador para fazer o upload dos arquivos? Obrigado e um feliz natal para toda família.

    • andrealveslima disse:

      Olá André, obrigado pelo comentário!

      Para checar se existe conexão com a Internet você pode utilizar uma das metodologias apresentadas nesta thread do StackOverflow:

      What is the best way to check for Internet connectivity using .NET?

      E quanto ao esquema de executar uma tarefa em um horário determinado (nesse caso a tarefa seria o upload do arquivo), existem diversas maneiras de implementar esse tipo de funcionalidade.. Você poderia, por exemplo, criar um console application que faria o upload do arquivo e agendar a execução dessa aplicação no agendador de tarefas do Windows.. Ou você poderia fazer isso diretamente na aplicação através de timers (mas aí a tarefa só seria executada se a aplicação estivesse sendo executada naquele momento)..

      Enfim, nesta outra thread do StackOverflow o pessoal dá uma discutida sobre as possíveis maneiras de implementar um agendador de tarefas no C#:

      How to call a method daily, at specific time, in C#?

      Abraço!
      André Lima

  • José Carlos R Leoner disse:

    André. Que você tenha um feliz natal e um e próspero ano novo. Muito obrigado por compartilhar seus conhecimentos que tem me ajudado bastante. Grande abraço.

    • andrealveslima disse:

      Olá José, muito obrigado!

      Desejo a você e sua família um 2017 com muita paz, saúde e sucesso! Obrigado por acompanhar os artigos nesse ano.. :)

      Um grande abraço!
      André Lima

  • Jose Felipe disse:

    André, ótimo post. Parabéns.

    Uma dúvida: Se o arquivo for muito grande (ex: 100MB), como proceder?

    Obrigado.

    • andrealveslima disse:

      Olá José, obrigado pelo comentário!

      Nesse caso, você não teria muitas opções.. O que o pessoal costuma fazer nessas situações é mostrar uma barra de progresso para o usuário ter uma noção de quanto já foi enviado e quanto está faltando..

      Para ver como exibir o progresso nas transferências com o FtpWebRequest, confira esta thread do StackOverflow:

      How can we show progress bar with FtpWebRequest

      Já com a biblioteca FluentFTP, tem um exemplo no GitHub da biblioteca que mostra como exibir a porcentagem da transferência:

      FluentFTP BeginOpenRead

      Abraço!
      André Lima

  • Fabiano Banin disse:

    Olha eu aqui de novo, eu precisei um tempo atrás trabalhar com FTP, como o servidor é interno da empresa eu acabei usando o FtWebRequest mesmo. Funcionou muito bem para mim, consigo listar os arquivos, fazer download e excluir os mesmos do servidor. Tive alguns problemas com o proxy, mas depois achei uma maneira de contornar e hoje ele acabou ficando desse jeito! O único problema que tive é que o FtpWebRequest não implementa servidores SFTP, isso me atrapalhou muito quando precisei capturar arquivos do FTP de um cliente em Dubai. Mas acabamos desistindo porque a comunicação do Brasil pra lá é um tanto complicada… para resolver isso combinamos de enviar o arquivo via e-mail! :D Por isso usei aquele seu post de e-mails!

    • andrealveslima disse:

      Olá Fabiano, obrigado pelo comentário!

      Eu achei algumas coisas bem mais complicadas de fazer com o FtpWebRequest do que com a biblioteca FluentFTP.. No final das contas dá para fazer tudo com o FtpWebRequest (como você acabou fazendo), mas temos que acabar escrevendo bem mais código para conseguir fazer a mesma coisa.. Mas, de qualquer forma, se ele atendeu às suas demandas (tirando o esquema do SFTP), não tem porque trocar..

      Ah, e fico feliz que o meu artigo sobre envio de e-mails tenha acabado ajudando nessa alternativa que vocês utilizaram.. :)

      Um grande abraço!
      André Lima

  • Manoel Pedro Jr disse:

    André Ótimo artigo, como poderia ser feito com o o FluentFTP para mostrar o progresso da transferência? em Percentual ou mesmo em uma barra de progresso?

  • Emanuel disse:

    Obrigado!André suas postagens e viddos são sempre muito bons ..valeu! !!!um exemplo de profissionalá humilde e simples. ..

  • André Luiz disse:

    Boa tarde, utilizo muito o servidor FTP para atualizar os programas nos clientes, atualmente fiz uma base de dados simples em texto mesmo pois a leitura de arquivo em xls exigia que o computador do cliente tivesse o pacote office instalado também..

    mas o que achei dificil é upload de arquivo, caso exista algum problema de conexão o arquivo fica corrompido no FTP. e também o download do Arquivo do FTP não indica a porcentagem…

    mas mesmo assim uso.. pois utilizo mais para download do que upload

    • andrealveslima disse:

      Olá André!

      Esse problema de arquivo corrompido é complicado mesmo.. Com o protocolo FTP (até onde eu sei), não dá para garantir se o arquivo foi transferido 100% com sucesso ou não.. Se você precisar de algo mais robusto, com garantia de entrega, acho que a saída seria partir para HTTP mesmo, criando um web service para fazer a comunicação entre a aplicação e o servidor..

      A porcentagem de transferência até que dá para mostrar.. Eu respondi um outro comentário mais acima (do Jose Felipe) passando links de como fazer isso nessas duas bibliotecas..

      Abraço!
      André Lima

  • Charles Cruz disse:

    Parabéns André por mais um excelente conteúdo.

  • Charles Cruz disse:

    Bom dia André fui instalar o Fluent no VS2013 Professional por comando no NuGet Console “Install-Package FluentFTP”;

    e ocorreu o seguinte erro:

    ——————————————————————————–

    Install-Package : ‘FluentFTP’ already has a dependency defined for ‘System.Runtime’.
    At line:1 char:1
    + Install-Package FluentFTP
    + ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Install-Package], InvalidOperationException
    + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PowerShell.Commands.InstallPackageCommand

    ——————————————————————————–

    fui na reference e adicionei todas as Referências “System.Runtime.*” que tinha mas o problema continuou, peço sua ajuda para solucionar o problema, grato desde já.

    • andrealveslima disse:

      Olá novamente, Charles!

      Esse tipo de erro ao adicionar referências com o NuGet normalmente acontece quando o NuGet está desatualizado.. Você está com o último Update do Visual Studio 2013 instalado? Já deu uma olhada na janela “Extensions and Updates” para ver se não tem nenhum update pendente?

      Além disso, qual é o tipo de projeto que você está tentando adicionar a referência? Console? Windows Forms? WPF? Web? É projeto novo ou um projeto já existente? Se o problema for em um projeto existente, você já tentou fazer a mesma coisa em um projeto do zero para ver se funciona?

      Abraço!
      André Lima

      • Charles Cruz disse:

        Olá André bom dia…
        O projeto é Windows Forms mesmo, segui sua dica e identifiquei que meu VS2013 não estava atualizado, após a atualização funcionou perfeitamente, muito obrigado pela atenção e ajuda.

        • andrealveslima disse:

          Olá Charles! Que bom que deu certo com o update do Visual Studio.. Qualquer outro problema, é só entrar em contato novamente..

          Abraço!
          André Lima

  • Lucas de Oliveira disse:

    Olá André, bom dia,

    Como que ficaria o código para fazer download de vários arquivos de uma pasta do meu FTP? modifiquei os códigos que vi na net (eu estou fazendo testes para um projeto de download da minha faculdade) e não consegui baixar todos os arquivos, apenas um por um.

    Então você pode me ajudar?

    • andrealveslima disse:

      Olá Lucas!

      Você não faz o download de múltiplos arquivos com FTP, você precisa fazer o download arquivo por arquivo mesmo.. Se você reparar em clientes FTP (como FileZilla, por exemplo), se você coloca para baixar um arquivo, ele vai baixar um de cada vez.. Claro que existe a possibilidade de baixar mais de um arquivo ao mesmo tempo, criando diferentes threads para fazer o download de cada arquivo..

      Mas, eu não consegui entender muito bem aonde é que você está tendo dificuldade.. Se você conseguiu baixar um arquivo, para baixar todos os arquivos você só precisa listar o conteúdo do FTP (que eu mostrei no artigo como fazer) e, através de um “foreach”, você faz o download arquivo por arquivo..

      Você poderia explicar com maiores detalhes aonde é que você está tendo dificuldade exatamente?

      Abraço!
      André Lima

  • André disse:

    Olá André. Como eu faço para baixar do FTP todos os arquivos e pastas e sub pastas com seus arquivos dentro?
    Estou tentando fazer mas não estou conseguindo.

    • andrealveslima disse:

      Olá André!

      Você está trabalhando com o FtpWebRequest ou com a biblioteca FluentFTP? Se for com a biblioteca FluentFTP, eu mostrei no artigo como listar o conteúdo de um diretório recursivamente.. Aí é só pegar cada um dos itens listados e baixar um por um.. Você tentou fazer dessa maneira?

      Abraço!
      André Lima

  • Murilo disse:

    Olá Andre, preciso limitar a taxa de transferencia dos arquivos no fluentftp, li na documentaçao dele sobre o UploadRateLimit, mas a taxa nao é
    respeitada, existe outra solução para realizar essa tarefa ?

    • andrealveslima disse:

      Olá Murilo!

      Nunca implementei essa funcionalidade, mas aparentemente são as propriedades UploadRateLimit e DownloadRateLimit que possibilitam isso mesmo.. Porém, como a documentação já diz, essas propriedades só são consideradas pelas APIs principais do FluentFTP (ou seja, os métodos principais como Upload, Download, UploadFile, etc).. Como é que ficou o seu código no final das contas?

      Além disso, essa funcionalidade só foi implementada em Maio desse ano (a partir desse pull request).. Você está utilizando uma versão superior a 17.4.2?

      Abraço!
      André Lima

  • Fabio disse:

    Ola Andre, estou com um projeto que envolve o seguinte processo:
    Criar diretorio e subdiretorio no computador local – Feito
    Acessar o endereço ftp remoto e baixar alguns arquivos .exe e copia-los para o subdiretorio da maquina local – esta exibindo erro.

    A pergunta é, como faço para baixar todos os .exe de um diretorio que está no ftp remoto para uma maquina local ?

    • andrealveslima disse:

      Olá Fabio!

      Não tem muito segredo.. Você precisa listar o conteúdo do diretório (mostrei no artigo) e baixar todos os arquivos que tenham extensão exe.. Qual é exatamente o erro que você está recebendo? Será que não tem nenhum antivirus bloqueando por ser um arquivo exe?

      Abraço!
      André Lima

  • Lucas disse:

    Andre,

    encontrei seu blog pois fiz um upload FTP, na minha máquina funcionou beleza, fui testar na maquina do funcionario que tem proxy e deu erro.. como ficaria a parte de logar no proxy para o FtpWebRequest e FluentFtp nesse caso??

  • Kayo Alves disse:

    Olá André, tudo bem?
    Passando pra dizer que utilizando essa metodologia de upload através do FluentFTP. Até funciona, lê o arquivo e o envia pro servidor. Mas ocorre algo semelhante no seu tópico acima, pois o arquivo vai corrompido (exceto txt). Lembrando que uso o servidor smarterasp.net. No entanto, vale destacar que atualmente a versão está na 24.0.0.0, então pode ter influenciado; mas a notícia boa é que funcionou corretamente com o seguinte código (disponível na documentação deles):
    https://github.com/robinrodricks/FluentFTP/blob/master/README.md

    Código:

    //Método
    private static void UploadFluent(string arquivo, string destino)
    {
    using (var client = new FluentFTP.FtpClient())
    {
    client.Host = “ftp.seuservidor.com”;
    client.Credentials = new
    System.Net.NetworkCredential(“seuusuário”, “suasenha”);

    /*essas três linhas abaixo que substituíram a utilização do ReadAllBytes*/
    client.Connect();
    client.UploadFile(arquivo, destino);
    client.Disconnect();
    }
    }

    Vale lembrar que lá informa que não precisa mais utilizar as credenciais caso seja anônimo. Ou seja, só remover o método NetworkCredential.
    Quanto ao download funcionou pra mim tanto o que mencionou quanto ao que tem na documentação deles:

    client.Connect();
    client.DownloadFile(“arquivo.extensao”, “arquivo.extensao”);
    client.Disconnect();

    Por fim, obrigado por sua postagem e por todo o conhecimento que você transmite, tem sido todos de grande ajuda. Forte abraço.

    • andrealveslima disse:

      Olá Kayo! Muito obrigado por ter tomado um tempo e postado os ajustes que você fez para o seu projeto. Com certeza ajudará outras pessoas que passem por esse mesmo problema. Valeu!

      Abraço,
      André

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *