Olá pessoal,
Boa tarde!

Neste post, vou falar novamente sobre o CLR (C#), demonstrando mais uma utilidade desse poderoso recurso do SQL Server, que é a integração entre o banco de dados e servidores FTP, seja para baixar (download) ou subir (upload) arquivos entre uma pasta local ou da sua rede e o servidor FTP.

Esse recurso é especialmente útil quando você precisar integrações com outras empresas, onde você precisa exportar dados e disponibilizá-los em um FTP externo de outra empresa ou importar arquivos de um FTP e importar para a sua rede ou para o seu banco de dados.

Para retornar mensagens de alerta e mensagens de erro do CLR para o banco de dados, utilizo a classe Retorno, que está disponível no post SQL Server – Como enviar avisos e mensagens de erro para o banco pelo CLR (C#) e é uma dependência dessa classe de FTP.

Em todas as rotinas, coloquei um “retry” de até 10 tentativas em casos de falha de conexão ou timeout, de forma que a sua rotina não apresente falha por alguma instabilidade da rede (isso já me ajudou MUITO).

Caso você não conheça o CLR ou não saiba por onde começar, veja mais acessando o post Introdução ao SQL CLR (Common Language Runtime) no SQL Server.

Classe Base – FTPControle.cs

Visualizar mais detalhes, exemplos de uso e código-fonte
Essa classe FTPControle é a classe base desse post, pois todas as Procedures e Functions desse post, necessitam dessa classe para funcionar. A ideia é colocar toda a lógica do controle do FTP nessa classe base, e as procedures e functions apenas utilizam os métodos dessa classe.

Apesar do código ser bem grande, ela não é uma classe complexa, e pode ser facilmente compreendida.

using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Globalization;
using System.Net;
using System.IO;

namespace Bibliotecas.Model
{

    public class FTPControle
    {

        public string Host { get; set; }
        public string Login { get; set; }
        public string Senha { get; set; }

        private FtpWebRequest _ftp;

        public FTPControle(string host, string login, string senha)
        {
            Host = host;
            Login = login;
            Senha = senha;
        }

        public void ApagarArquivos(string pastaFtp, List<string> arquivos)
        {
            foreach (var arquivo in arquivos)
            {
                var arquivoFtp = Host + pastaFtp + arquivo;
                _ftp = (FtpWebRequest)WebRequest.Create(arquivoFtp);

                IniciarMetodo();

                _ftp.Method = WebRequestMethods.Ftp.DeleteFile;
                _ftp.GetResponse();
            }
        }

        public void ApagarArquivo(string pastaFtp, string arquivo)
        {
            var arquivoFtp = Host + pastaFtp + arquivo;
            _ftp = (FtpWebRequest)WebRequest.Create(arquivoFtp);

            IniciarMetodo();

            _ftp.Method = WebRequestMethods.Ftp.DeleteFile;
            _ftp.GetResponse();

        }

        public List<string> ListarArquivos(string pastaFtp, string filtro)
        {
            var _pastaFtp = Host + pastaFtp;
            _ftp = (FtpWebRequest)WebRequest.Create(_pastaFtp);
            var arquivos = new List<string>();

            IniciarMetodo();

            _ftp.Method = WebRequestMethods.Ftp.ListDirectory;

            using (var response = (FtpWebResponse) _ftp.GetResponse())
            {

                using (var responseStream = response.GetResponseStream())
                {

                    if (responseStream != null)
                    {
                        using (var r = new StreamReader(responseStream))
                        {

                            var s = r.ReadLine();

                            while (s != null)
                            {
                                arquivos.Add(s);
                                s = r.ReadLine();
                            }

                        }

                    }

                }

            }

            return arquivos.FindAll(
                nome => nome.Contains(filtro.Replace("*", ""))
            );
        }

        public List<string> ListarArquivosCompleto(string pastaFtp, string filtro)
        {

            var _pastaFtp = Host + pastaFtp;
            _ftp = (FtpWebRequest) WebRequest.Create(_pastaFtp);
            var arquivos = new List<string>();

            IniciarMetodo();

            _ftp.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

            using (var response = (FtpWebResponse) _ftp.GetResponse())
            {

                using (var responseStream = response.GetResponseStream())
                {

                    if (responseStream != null)
                    {

                        using (var r = new StreamReader(responseStream))
                        {
                            var s = r.ReadLine();
                            while (s != null)
                            {
                                arquivos.Add(s);
                                s = r.ReadLine();
                            }
                        }

                    }
                    
                }

            }

            return arquivos.FindAll(
                nome => nome.Contains(filtro.Replace("*", ""))
            );

        }



        private static string GetOctalPermission(string Ds_Permissao)
        {

            switch (Ds_Permissao)
            {

                case "---":
                    return "0";

                case "--x":
                    return "1";

                case "-w-":
                    return "2";

                case "-wx":
                    return "3";

                case "r--":
                    return "4";

                case "r-x":
                    return "5";

                case "rw-":
                    return "6";

                case "rwx":
                    return "7";

                default:
                    return "";
            }

        }


        private static int GetMonth(string Ds_Mes)
        {

            switch (Ds_Mes.ToLower())
            {

                case "jan":
                    return 1;

                case "feb":
                    return 2;

                case "mar":
                    return 3;

                case "apr":
                    return 4;

                case "may":
                    return 5;

                case "jun":
                    return 6;

                case "jul":
                    return 7;

                case "aug":
                    return 8;

                case "sep":
                    return 9;

                case "oct":
                    return 10;

                case "nov":
                    return 11;

                case "dec":
                    return 12;

                default:
                    return 0;
            }

        }


        private static string GetMonthName(string Ds_Mes)
        {

            switch (Ds_Mes)
            {

                case "1":
                    return "jan";

                case "2":
                    return "feb";

                case "3":
                    return "mar";

                case "4":
                    return "apr";

                case "5":
                    return "may";

                case "6":
                    return "jun";

                case "7":
                    return "jul";

                case "8":
                    return "aug";

                case "9":
                    return "sep";

                case "10":
                    return "oct";

                case "11":
                    return "nov";

                case "12":
                    return "dez";

                default:
                    return "";
            }

        }


        public FTPArquivoListar ListarArquivosCompleto_Parse(string arquivo, string Ds_Pasta_FTP, int Nr_Contador)
        {

            try
            {

                var dir = (arquivo.Substring(0, 1) == "d") ? "d" : "f";
                var owner = "";
                var group = "";
                var ownerSec = "";
                var groupSec = "";
                var everyoneSec = "";
                var size = 0;
                var month = "";
                var year = "";
                var day = "";
                var filename = "";


                SqlDateTime data;

                if (arquivo.Substring(15, 2).ToUpper() == "AM" || arquivo.Substring(15, 2).ToUpper() == "PM")
                {

                    dir = arquivo.Contains("<DIR>") ? "d" : "f";

                    int.TryParse(arquivo.Substring(29, 9).Trim(), out size);

                    month = arquivo.Substring(0, 2);
                    day = arquivo.Substring(3, 2);
                    year = arquivo.Substring(6, 2);

                    year = (int.Parse(year) > DateTime.Now.Year ? "19" : "20") + year;

                    var hora = int.Parse(arquivo.Substring(10, 2)) + (arquivo.Substring(15, 2).ToUpper() == "PM" && int.Parse(arquivo.Substring(10, 2)) < 12 ? 12 : 0);
                    var minuto = int.Parse(arquivo.Substring(13, 2));

                    data = new DateTime(int.Parse(year), int.Parse(month), int.Parse(day), hora, minuto, 0);
                    
                    filename = arquivo.Substring(39, arquivo.Length - 39).Trim();

                }
                else
                {

                    var palavras = arquivo.Split(' ');
                    var contador = 1;

                    ownerSec = arquivo.Substring(1, 3).Trim();
                    groupSec = arquivo.Substring(4, 3).Trim();
                    everyoneSec = arquivo.Substring(7, 3).Trim();

                    foreach (var palavra in palavras)
                    {

                        if (contador == 3)
                        {
                            owner = palavra.Trim();
                        }
                        else if (contador == 4)
                        {
                            group = palavra.Trim();
                        }
                        else if (contador == 5)
                        {
                            int.TryParse(palavra.Trim(), out size);
                        }
                        else if (contador == 6)
                        {
                            month = palavra.ToLower().Trim();
                        }
                        else if (contador == 7)
                        {
                            day = palavra.Trim();
                        }
                        else if (contador == 8)
                        {
                            year = palavra.Trim();
                        }
                        else
                        {
                            if (contador >= 9)
                            {
                                filename += palavra;
                            }
                        }

                        if (!string.IsNullOrEmpty(palavra) || contador >= 9)
                            contador++;

                    }


                    if (year.IndexOf(":", StringComparison.Ordinal) >= 0)
                    {

                        var mes = GetMonth(month);

                        var time = year.Split(':');
                        data = mes > DateTime.Now.Month ? new SqlDateTime(DateTime.Now.Year - 1, mes, Convert.ToInt32(day), Convert.ToInt32(time[0]), Convert.ToInt32(time[1]), 0) : new SqlDateTime(DateTime.Now.Year, mes, Convert.ToInt32(day), Convert.ToInt32(time[0]), Convert.ToInt32(time[1]), 0);
                    }
                    else
                    {
                        var dataOrigem = DateTime.ParseExact(year + "-" + month + "-" + day, "yyyy-MMM-d", new CultureInfo("en-US"));
                        data = Convert.ToDateTime(dataOrigem.ToString("dd/MM/yyyy", new CultureInfo("pt-BR")));
                    }

                }


                if (filename == "." || filename == "..")
                {
                    return new FTPArquivoListar(
                        0,
                        "Diretorio",
                        Ds_Pasta_FTP + "/",
                        filename, //arquivo,
                        size,
                        SqlDateTime.Null,
                        ownerSec + groupSec + everyoneSec,
                        "0" + GetOctalPermission(ownerSec) + GetOctalPermission(groupSec) + GetOctalPermission(everyoneSec),
                        owner,
                        group
                    );
                }

                
                return new FTPArquivoListar(
                    Nr_Contador,
                    (dir == "d" ? "Diretorio" : "Arquivo"),
                    Ds_Pasta_FTP + "/",
                    filename, //arquivo,
                    size,
                    data,
                    ownerSec + groupSec + everyoneSec,
                    "0" + GetOctalPermission(ownerSec) + GetOctalPermission(groupSec) + GetOctalPermission(everyoneSec),
                    owner,
                    group
                );

            }
            catch (Exception e)
            {

                return new FTPArquivoListar(
                    0,
                    "Erro",
                    arquivo,
                    e.Message, //arquivo,
                    0,
                    SqlDateTime.Null,
                    "",
                    "000",
                    "",
                    ""
                );

            }

        }


        public int BaixarArquivos(string pastaFtp, List<string> arquivos, string pastaLocal)
        {

            var buffer = new byte[2048];

            foreach (var arquivo in arquivos)
            {
                var arquivoFtp = Host + pastaFtp + arquivo;
                var arquivoLocal = pastaLocal + arquivo;
                _ftp = (FtpWebRequest)WebRequest.Create(arquivoFtp);

                IniciarMetodo();

                _ftp.Method = WebRequestMethods.Ftp.DownloadFile;

                using (var response = (FtpWebResponse)_ftp.GetResponse())
                {

                    using (var responseStream = response.GetResponseStream())
                    {

                        using (var fs = new FileStream(arquivoLocal, FileMode.Create))
                        {

                            if (responseStream != null)
                            {

                                var read = responseStream.Read(buffer, 0, buffer.Length); ;
                                while (read != 0)
                                {
                                    fs.Write(buffer, 0, read);
                                    read = responseStream.Read(buffer, 0, buffer.Length);
                                }
                            }

                            fs.Flush();
                            
                        }
                        
                    }
                    
                }

            }

            return arquivos.Count;

        }

        public void BaixarArquivo(string pastaFtp, string arquivo, string arquivoLocal)
        {

            var buffer = new byte[2048];

            var arquivoFtp = Host + pastaFtp + arquivo;
            var _arquivoLocal = arquivoLocal;
            _ftp = (FtpWebRequest) WebRequest.Create(arquivoFtp);

            IniciarMetodo();

            _ftp.Method = WebRequestMethods.Ftp.DownloadFile;

            using (var response = (FtpWebResponse) _ftp.GetResponse())
            {
                using (var responseStream = response.GetResponseStream())
                {
                    using (var fs = new FileStream(_arquivoLocal, FileMode.Create))
                    {

                        if (responseStream != null)
                        {
                            var read = responseStream.Read(buffer, 0, buffer.Length); ;
                            while (read != 0)
                            {
                                fs.Write(buffer, 0, read);
                                read = responseStream.Read(buffer, 0, buffer.Length);
                            }
                        }

                        fs.Flush();

                    }

                }
                
            }

        }

        public int SubirArquivos(List<string> arquivos, string pastaFtp)
        {
            foreach (var arquivo in arquivos)
            {
                string nomeArquivo;
                if (arquivo.LastIndexOf(@"/", StringComparison.Ordinal) > 0)
                    nomeArquivo = arquivo.Substring(arquivo.LastIndexOf(@"/", StringComparison.Ordinal),arquivo.Length - arquivo.LastIndexOf(@"/", StringComparison.Ordinal));
                else
                    if (arquivo.LastIndexOf(@"\", StringComparison.Ordinal) > 0)
                        nomeArquivo = arquivo.Substring(arquivo.LastIndexOf(@"\", StringComparison.Ordinal), arquivo.Length - arquivo.LastIndexOf(@"\", StringComparison.Ordinal));
                    else
                        return -1;

                var arquivoFtp = Host + pastaFtp + nomeArquivo;
                var arquivoLocal = arquivo;
                _ftp = (FtpWebRequest)WebRequest.Create(arquivoFtp);

                IniciarMetodo();

                _ftp.Method = WebRequestMethods.Ftp.UploadFile;
                _ftp.Timeout = 600000;

                byte[] buffer;
                using (var stream = File.OpenRead(arquivoLocal))
                {
                    buffer = new byte[stream.Length];
                    stream.Read(buffer, 0, buffer.Length);
                }

                using (var reqStream = _ftp.GetRequestStream())
                {
                    reqStream.Write(buffer, 0, buffer.Length);
                }

            }

            return arquivos.Count;

        }

        public void SubirArquivo(string arquivoLocal, string pastaFtp, string nomeArquivoRemoto)
        {
            
            var arquivoFtp = Host + pastaFtp + nomeArquivoRemoto;
            var _arquivoLocal = arquivoLocal;
            _ftp = (FtpWebRequest)WebRequest.Create(arquivoFtp);

            IniciarMetodo();

            _ftp.Method = WebRequestMethods.Ftp.UploadFile;
            _ftp.Timeout = 600000;

            byte[] buffer;
            using (var stream = File.OpenRead(_arquivoLocal))
            {
                buffer = new byte[stream.Length];

                stream.Read(buffer, 0, buffer.Length);
            }

            using (var reqStream = _ftp.GetRequestStream())
            {
                reqStream.Write(buffer, 0, buffer.Length);
            }

        }

        public FtpWebResponse CriaDiretorio(string pastaFtp)
        {

            var arquivoFtp = Host + "/" + pastaFtp;
            _ftp = (FtpWebRequest) WebRequest.Create(arquivoFtp);

            IniciarMetodo();

            _ftp.Method = WebRequestMethods.Ftp.MakeDirectory;

            using (var response = (FtpWebResponse) _ftp.GetResponse())
            {
                return response;
            }
        }

        public FtpWebResponse ApagaArquivo(string pastaFtp, string arquivoRemoto)
        {

            var arquivoFtp = Host + "/" + pastaFtp + "/" + arquivoRemoto;
            _ftp = (FtpWebRequest) WebRequest.Create(arquivoFtp);

            IniciarMetodo();

            _ftp.Method = WebRequestMethods.Ftp.DeleteFile;

            using (var response = (FtpWebResponse) _ftp.GetResponse())
            {
                return response;
            }
        }

        public FtpWebResponse ApagaDiretorio(string pastaFtp, bool apagaArquivos)
        {

            pastaFtp = ("/" + pastaFtp + "/").Replace("//", "/");
            
            if (apagaArquivos)
            {

                var filesList = ListarArquivos(pastaFtp, "*.*");

                foreach (var file in filesList)
                {
                    ApagaArquivo(pastaFtp, file);
                }
            }


            var arquivoFtp = Host + pastaFtp;
            _ftp = (FtpWebRequest)WebRequest.Create(arquivoFtp);

            IniciarMetodo();


            _ftp.Method = WebRequestMethods.Ftp.RemoveDirectory;

            using (var response = (FtpWebResponse) _ftp.GetResponse())
            {
                return response;
            }

        }

        private void IniciarMetodo()
        {
            //_ftp.Proxy = new WebProxy(Servidor.Ds_Proxy_Url, Servidor.Ds_Proxy_Porta) { Credentials = new NetworkCredential(Servidor.Ds_Proxy_Usuario, Servidor.Ds_Proxy_Senha, Servidor.Ds_Proxy_Dominio) };
            _ftp.Proxy = null;
            _ftp.Credentials = new NetworkCredential(Login, Senha);
            _ftp.KeepAlive = false;
            _ftp.UseBinary = true;
            _ftp.UsePassive = true;
        }

    }


    public class FTPArquivoListar
    {

        public SqlInt32 Nr_Linha;
        public SqlString Nm_Arquivo;
        public SqlString Nm_Diretorio;
        public SqlString Fl_Tipo;
        public SqlInt64 Qt_Tamanho;
        public SqlDateTime Dt_Criacao;
        public SqlString Ds_Permissao;
        public SqlString Ds_Permissao_Octal;
        public SqlString Ds_Proprietario;
        public SqlString Ds_Grupo;

        public FTPArquivoListar(SqlInt32 nrLinha, SqlString nmArquivo, SqlString nmDiretorio, SqlString flTipo, SqlInt64 qtTamanho, SqlDateTime dtCriacao, SqlString dsPermissao, SqlString dsPermissaoOctal, SqlString dsProprietario, SqlString dsGrupo)
        {

            Nr_Linha = nrLinha;
            Nm_Arquivo = nmArquivo;
            Nm_Diretorio = nmDiretorio;
            Fl_Tipo = flTipo;
            Qt_Tamanho = qtTamanho;
            Dt_Criacao = dtCriacao;
            Ds_Permissao = dsPermissao;
            Ds_Permissao_Octal = dsPermissaoOctal;
            Ds_Proprietario = dsProprietario;
            Ds_Grupo = dsGrupo;

        }

    }

}

Como listar arquivos de um FTP pelo SQL Server

Visualizar mais detalhes, exemplos de uso e código-fonte
Através da stored procedure abaixo, agora é possível listar todos os arquivos e diretórios presentes em um servidor FTP. Essa informação será muito útil para identificar se um arquivo realmente foi enviado para o servidor ou mesmo para listar os arquivos que existem em um diretório para baixá-los depois.

O parâmetro filtro permite informar o nome do arquivo que se desejar filtrar ou utilizar um Wildcard para retornar apenas os arquivos que atendam ao critério informado (Ex: “*” retorna todos os arquivos, “*.txt” retorna todos os arquivos terminados com .txt, etc..)

Exemplo de uso:

microsoft-sql-server-clr-integration-with-ftp-list-files-and-directories
microsoft-sql-server-clr-integration-with-ftp-list-files-and-directories

Código-fonte:

using System.Data;
using System.Data.SqlTypes;
using System.Net;
using System.Threading;
using Microsoft.SqlServer.Server;
using Bibliotecas.Model;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void stpFTP_Arquivo_Listar(SqlString host, SqlString pastaFtp, SqlString filtro, SqlString login, SqlString senha)
    {

        const int tentativas = 10;
        var sucesso = false;

        for (var i = 1; i <= tentativas; i++)
        {

            try
            {

                var ftp = new FTPControle(host.Value, login.Value, senha.Value);
                var arquivos = ftp.ListarArquivos(pastaFtp.Value, filtro.Value);

                var pipe = SqlContext.Pipe;
                var cols = new SqlMetaData[1];
                cols[0] = new SqlMetaData("Nm_Arquivo", SqlDbType.NVarChar, 1024);

                var rec = new SqlDataRecord(cols);
                if (pipe == null) return;

                pipe.SendResultsStart(rec);

                foreach (var file in arquivos)
                {
                    rec.SetSqlString(0, new SqlString(file));
                    pipe.SendResultsRow(rec);
                }

                pipe.SendResultsEnd();
                sucesso = true;
            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.ConnectFailure || e.Status == WebExceptionStatus.SendFailure || e.Status == WebExceptionStatus.ReceiveFailure || e.Status == WebExceptionStatus.PipelineFailure || e.Status == WebExceptionStatus.ConnectionClosed)
                {
                    if (i < tentativas)
                        Retorno.Mensagem($"Erro na tentativa {i}: {e.Message}");
                    else
                        Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
                }
                else
                    Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
            }


            if (sucesso)
                break;


            Thread.Sleep(10000);


        }

    }

};

Função para listar arquivos de um FTP pelo SQL Server

Visualizar mais detalhes, exemplos de uso e código-fonte
Apesar de já ter demonstrado como listar arquivos, essa função do tipo tabular (table-valued) permite identificar uma série de informações adicionais sobre os arquivos e diretórios do servidor FTP, como data de criação, tamanho, permissões, etc.

Além disso, ela pode ser utilizada facilmente para filtrar com WHERE, utilizar JOINS, ORDER BY, SELECT * INTO #Tabela, etc, dando muito mais flexibilidade na sua utilização em relação à Stored Procedure anterior.

Exemplo de uso:

microsoft-sql-server-clr-integration-with-ftp-function-to-list-files-and-directories
microsoft-sql-server-clr-integration-with-ftp-function-to-list-files-and-directories

Código-fonte:

using System.Collections;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Net;
using System.Threading;
using Bibliotecas.Model;

public partial class UserDefinedFunctions
{

    [Microsoft.SqlServer.Server.SqlFunction(
        FillRowMethodName = "FillRow_FTP_Arquivos_Listar",
        TableDefinition = "Nr_Linha INT, Fl_Tipo NVARCHAR(20), Nm_Diretorio NVARCHAR(500), Nm_Arquivo NVARCHAR(500), " +
                            "Qt_Tamanho BIGINT, Dt_Criacao DATETIME, Ds_Permissao NVARCHAR(20), Ds_Permissao_Octal NVARCHAR(4), " +
                            "Ds_Proprietario NVARCHAR(20), Ds_Grupo NVARCHAR(20)"
    )]
    public static IEnumerable fncFTP_Arquivos_Listar(string Ds_Hostname, string Ds_Usuario, string DsSenha, string Ds_Pasta_FTP, string Ds_Filtro)
    {

        var ftpArquivoListarCollection = new ArrayList();

        if (string.IsNullOrEmpty(Ds_Hostname))
            return ftpArquivoListarCollection;


        var ftp = new FTPControle(Ds_Hostname, Ds_Usuario, DsSenha);
        var arquivos = new List<string>();


        const int tentativas = 10;
        var sucesso = false;
        
        for (var i = 1; i <= tentativas; i++)
        {

            try
            {
                arquivos = ftp.ListarArquivosCompleto(Ds_Pasta_FTP, Ds_Filtro);
                sucesso = true;
            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.ConnectFailure || e.Status == WebExceptionStatus.SendFailure || e.Status == WebExceptionStatus.ReceiveFailure || e.Status == WebExceptionStatus.PipelineFailure || e.Status == WebExceptionStatus.ConnectionClosed)
                {
                    if (i < tentativas)
                        Retorno.Mensagem($"Erro na tentativa {i}: {e.Message}");
                    else
                        Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
                }
                else
                    Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
            }

            if (sucesso)
                break;

            Thread.Sleep(10000);

        }


        var contador = 1;

        foreach (var arquivo in arquivos)
        {

            if (arquivo.Substring(0, 1) != "d") continue;

            var retorno = ftp.ListarArquivosCompleto_Parse(arquivo, Ds_Pasta_FTP, contador);
            if (retorno.Nr_Linha.Value == 0) continue;

            ftpArquivoListarCollection.Add(retorno);

            contador++;
        }



        foreach (var arquivo in arquivos)
        {

            if (arquivo.Substring(0, 1) == "d") continue;

            var retorno = ftp.ListarArquivosCompleto_Parse(arquivo, Ds_Pasta_FTP, contador);

            ftpArquivoListarCollection.Add(retorno);

            contador++;
        }


        return ftpArquivoListarCollection;

    }

    protected static void FillRow_FTP_Arquivos_Listar(object objFTPArquivoListar, out SqlInt32 nrLinha, out SqlString nmArquivo, out SqlString nmDiretorio, out SqlString flTipo, out SqlInt64 qtTamanho, out SqlDateTime dtCriacao, out SqlString dsPermissao, out SqlString dsPermissaoOctal, out SqlString dsProprietario, out SqlString dsGrupo)
    {

        var ftpArquivoListar = (FTPArquivoListar) objFTPArquivoListar;

        nrLinha = ftpArquivoListar.Nr_Linha;
        nmArquivo = ftpArquivoListar.Nm_Arquivo;
        nmDiretorio = ftpArquivoListar.Nm_Diretorio;
        flTipo = ftpArquivoListar.Fl_Tipo;
        qtTamanho = ftpArquivoListar.Qt_Tamanho;
        dtCriacao = ftpArquivoListar.Dt_Criacao;
        dsPermissao = ftpArquivoListar.Ds_Permissao;
        dsPermissaoOctal = ftpArquivoListar.Ds_Permissao_Octal;
        dsProprietario = ftpArquivoListar.Ds_Proprietario;
        dsGrupo = ftpArquivoListar.Ds_Grupo;

    }

}

Como baixar um arquivo de um servidor FTP pelo SQL Server

Visualizar mais detalhes, exemplos de uso e código-fonte
Utilizando essa Stored Procedure, você pode facilmente baixar um arquivo específico ou vários arquivos utilizando expressões regulares, conforme vou demonstrar abaixo.

Um parâmetro especialmente útil, é o @apagarRemoto, que quando é informado o valor “1”, realiza o download dos arquivos que atendem aos critérios dos filtros informados e, logo em seguida, apaga esses arquivos do servidor FTP.

Exemplos de uso:

Baixando o arquivo "about.php"
Baixando o arquivo "about.php"

Baixando o arquivo “about.php”

Fazendo o download de múltiplos arquivos utilizando LIKE
Fazendo o download de múltiplos arquivos utilizando LIKE

Fazendo o download de múltiplos arquivos utilizando filtros Wildcard

Resultado final com os arquivos baixados do FTP
Resultado final com os arquivos baixados do FTP

Resultado final com os arquivos baixados do FTP

Código-fonte:

using System.Data.SqlTypes;
using System.IO;
using System.Net;
using System.Threading;
using Bibliotecas.Model;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void stpFTP_Arquivo_Download(SqlString host, SqlString pastaFtp, SqlString filtro, SqlString login, SqlString senha, SqlString pastaLocal, SqlBoolean apagarRemoto)
    {

        #region tratamento de entradas

        try
        {
            if (!Directory.Exists(pastaLocal.Value))
            {
                Retorno.Erro("Pasta local especificada não existe ou inacessivel.");
                return;
            }
        }
        catch
        {
            Retorno.Erro("Erro ao converter pasta local especificada.");
            return;
        }

        #endregion


        const int tentativas = 10;
        var sucesso = false;

        for (var i = 1; i <= tentativas; i++)
        {

            try
            {

                var ftp = new FTPControle(host.Value, login.Value, senha.Value);
                var arquivos = ftp.ListarArquivos(pastaFtp.Value, filtro.Value);
                var qntArquivos = ftp.BaixarArquivos(pastaFtp.Value, arquivos, pastaLocal.Value);

                if (apagarRemoto.Value)
                {
                    ftp.ApagarArquivos(pastaFtp.Value, arquivos);
                }

                Retorno.Mensagem("Total de: (" + qntArquivos + ") arquivos foram baixados para: " + pastaLocal.Value);
                sucesso = true;
            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.ConnectFailure || e.Status == WebExceptionStatus.SendFailure || e.Status == WebExceptionStatus.ReceiveFailure || e.Status == WebExceptionStatus.PipelineFailure || e.Status == WebExceptionStatus.ConnectionClosed)
                {
                    if (i < tentativas)
                        Retorno.Mensagem($"Erro na tentativa {i}: {e.Message}");
                    else
                        Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
                }
                else
                    Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
            }


            if (sucesso)
                break;


            Thread.Sleep(10000);


        }
        
    }

};

Como enviar um arquivo para um servidor FTP pelo SQL Server

Visualizar mais detalhes, exemplos de uso e código-fonte
Utilizando essa Stored Procedure, você pode realizar o envio (upload) de arquivos do seu servidor local ou de arquivos que estão em caminhos de rede para um servidor FTP.

Novamente, a SP permite especificar o nome do arquivo no parâmetro @filtro para enviar um arquivo específico, ou utilizar wildcards para realizar filtros em nomes de arquivos e enviar vários arquivos em uma única execução da Stored Procedure.

Exemplo de uso:

Enviando todos os arquivos com extensão ".txt" para o servidor FTP
Enviando todos os arquivos com extensão ".txt" para o servidor FTP

Enviando todos os arquivos com extensão “.txt” para o servidor FTP

Código-fonte:

using System.Collections.Generic;
using System.Data.SqlTypes;
using System.IO;
using System.Net;
using System.Threading;
using Bibliotecas.Model;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void stpFTP_Arquivo_Upload(SqlString host, SqlString pastaFtp, SqlString login, SqlString senha, SqlString pastaLocal, SqlString filtro)
    {

        #region tratamento de entradas
        try
        {
            if (!Directory.Exists(pastaLocal.Value))
            {
                Retorno.Erro("Pasta local especificada não existe ou inacessivel.");
                return;
            }
        }
        catch
        {
            Retorno.Erro("Erro ao converter pasta local especificada.");
            return;
        }

        List<string> listaArquivos;
        try
        {
            var arquivos = Directory.GetFiles(pastaLocal.Value, filtro.Value);
            listaArquivos = new List<string>(arquivos);
            if (listaArquivos.Count == 0)
            {
                Retorno.Mensagem("(0) arquivos atendem o requisito de filtragem na pasta especificada.");
                return;
            }
        }
        catch
        {
            Retorno.Erro("Erro ao listar arquivos da pasta especificada.");
            return;
        }

        #endregion


        const int tentativas = 10;
        var sucesso = false;

        for (var i = 1; i <= tentativas; i++)
        {

            try
            {

                var ftp = new FTPControle(host.Value, login.Value, senha.Value);
                var qntArquivos = ftp.SubirArquivos(listaArquivos, pastaFtp.Value);

                if (qntArquivos != -1)
                {
                    Retorno.Mensagem("Total de: (" + qntArquivos + ") arquivos foram enviados para: " + pastaFtp.Value);
                    sucesso = true;
                }
                else
                    Retorno.Erro("Erro : Diretório do arquivo local inválido!");

            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.ConnectFailure || e.Status == WebExceptionStatus.SendFailure || e.Status == WebExceptionStatus.ReceiveFailure || e.Status == WebExceptionStatus.PipelineFailure || e.Status == WebExceptionStatus.ConnectionClosed)
                {
                    if (i < tentativas)
                        Retorno.Mensagem($"Erro na tentativa {i}: {e.Message}");
                    else
                        Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
                }
                else
                    Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
            }


            if (sucesso)
                break;


            Thread.Sleep(10000);

        }

    }

}

Como criar um diretório em um servidor FTP pelo SQL Server

Visualizar mais detalhes, exemplos de uso e código-fonte
Essa Stored Procedure tem como finalidade, permitir a criação de diretórios em um servidor FTP. Isso é muito importante, porque se você precisar subir arquivos em um novo diretório, informar esse diretório na hora de realizar o upload, sem tê-lo criado antes, irá causar uma mensagem de erro.

Utilizando as funções de listar arquivos e diretórios, é possível identificar se o diretório desejado existe ou não, e caso não exista, criá-lo utilizando essa SP.

Exemplo de uso:

microsoft-sql-server-clr-integration-with-ftp-how-to-create-diretory
microsoft-sql-server-clr-integration-with-ftp-how-to-create-diretory

Código-fonte:

using System.Data.SqlTypes;
using Bibliotecas.Model;
using System.Net;
using System.Threading;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void stpFTP_Cria_Diretorio(SqlString host, SqlString login, SqlString senha, SqlString pastaFtp)
    {

        const int tentativas = 10;
        var sucesso = false;

        for (var i = 1; i <= tentativas; i++)
        {

            try
            {

                var ftp = new FTPControle(host.Value, login.Value, senha.Value);
                using (var response = ftp.CriaDiretorio(pastaFtp.Value))
                {

                    if (response.StatusCode == FtpStatusCode.PathnameCreated)
                    {
                        Retorno.Mensagem("Diretório " + pastaFtp.Value + " criado com sucesso");
                        sucesso = true;
                    }
                    else
                    {
                        Retorno.Erro(response.StatusDescription);
                    }
                }

            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.ConnectFailure || e.Status == WebExceptionStatus.SendFailure || e.Status == WebExceptionStatus.ReceiveFailure || e.Status == WebExceptionStatus.PipelineFailure || e.Status == WebExceptionStatus.ConnectionClosed)
                {
                    if (i < tentativas)
                        Retorno.Mensagem($"Erro na tentativa {i}: {e.Message}");
                    else
                        Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
                }
                else
                    Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
            }


            if (sucesso)
                break;


            Thread.Sleep(10000);

        }

    }

};

Como apagar um arquivo de um servidor FTP pelo SQL Server

Visualizar mais detalhes, exemplos de uso e código-fonte
Stored Procedure que permite apagar um ou mais arquivos de um diretório FTP. Diferente das SP’s anteriores, não é possível utilizar um filtro para apagar vários arquivos. O parâmetro @arquivoRemoto deve receber o nome do arquivo que será apagado. Caso você queira apagar vários arquivos, utilize um WHILE para iterar entre os arquivos e chame a SP várias vezes.

Isso não ocorre por impossibilidade técnica, apenas achei mais “seguro” não permitir apagar vários arquivos utilizando um filtro, mas nada que o impeça de alterar essa Procedure para permitir isso ou utilizar a Stored Procedure stpFTP_Apaga_Diretorio, que está logo abaixo.

Exemplo de uso:

microsoft-sql-server-clr-integration-with-ftp-how-to-delete-files
microsoft-sql-server-clr-integration-with-ftp-how-to-delete-files

Código-fonte:

using System.Data.SqlTypes;
using Bibliotecas.Model;
using System.Net;
using System.Threading;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void stpFTP_Apaga_Arquivo(SqlString host, SqlString login, SqlString senha, SqlString pastaFtp, SqlString arquivoRemoto)
    {

        const int tentativas = 10;
        var sucesso = false;

        for (var i = 1; i <= tentativas; i++)
        {

            try
            {

                var ftp = new FTPControle(host.Value, login.Value, senha.Value);
                using (var response = ftp.ApagaArquivo(pastaFtp.Value, arquivoRemoto.Value))
                {

                    if (response.StatusCode == FtpStatusCode.CommandOK || response.StatusCode == FtpStatusCode.FileActionOK)
                    {
                        Retorno.Mensagem("Arquivo " + pastaFtp.Value + "/" + arquivoRemoto.Value + " apagado com sucesso");
                        sucesso = true;
                    }
                    else
                    {
                        Retorno.Erro(response.StatusDescription);
                    }
                }

            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.ConnectFailure || e.Status == WebExceptionStatus.SendFailure || e.Status == WebExceptionStatus.ReceiveFailure || e.Status == WebExceptionStatus.PipelineFailure || e.Status == WebExceptionStatus.ConnectionClosed)
                {
                    if (i < tentativas)
                        Retorno.Mensagem($"Erro na tentativa {i}: {e.Message}");
                    else
                        Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
                }
                else
                    Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
            }


            if (sucesso)
                break;


            Thread.Sleep(10000);

        }
    }
};

Como apagar um diretório de um servidor FTP pelo SQL Server

Visualizar mais detalhes, exemplos de uso e código-fonte
Utilizando essa Stored Procedure, você pode apagar diretórios de seu servidor FTP. Um detalhe essa SP, é o parâmetro @apagaArquivos, que caso você informe o valor 1, irá apagar todos os arquivos desse diretório para então, tentar apagar o diretório.

Tenha em mente, que caso você informe o valor do parâmetro @apagarArquivos = 0 e houverem arquivos no diretório que você está tentando apagar, a SP irá retornar uma mensagem de erro informando que existem arquivos no diretório.

Exemplo de uso:

microsoft-sql-server-clr-integration-with-ftp-how-to-delete-files-and-directory
microsoft-sql-server-clr-integration-with-ftp-how-to-delete-files-and-directory

Código-fonte:

using System.Data.SqlTypes;
using Bibliotecas.Model;
using System.Net;
using System.Threading;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void stpFTP_Apaga_Diretorio(SqlString host, SqlString login, SqlString senha, SqlString pastaFtp, SqlBoolean apagaArquivos)
    {

        const int tentativas = 10;
        var sucesso = false;

        for (var i = 1; i <= tentativas; i++)
        {

            try
            {

                var ftp = new FTPControle(host.Value, login.Value, senha.Value);
                using (var response = ftp.ApagaDiretorio(pastaFtp.Value, apagaArquivos.Value))
                {

                    if (response.StatusCode == FtpStatusCode.FileActionOK)
                    {
                        Retorno.Mensagem("Diretório " + pastaFtp.Value + " apagado com sucesso");
                        sucesso = true;
                    }
                    else
                    {
                        Retorno.Erro(response.StatusDescription);
                    }
                }

            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.ConnectFailure || e.Status == WebExceptionStatus.SendFailure || e.Status == WebExceptionStatus.ReceiveFailure || e.Status == WebExceptionStatus.PipelineFailure || e.Status == WebExceptionStatus.ConnectionClosed)
                {
                    if (i < tentativas)
                        Retorno.Mensagem($"Erro na tentativa {i}: {e.Message}");
                    else
                        Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
                }
                else
                    Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}");
            }


            if (sucesso)
                break;


            Thread.Sleep(10000);

        }
    }
};

É isso aí, pessoal!
Espero que tenham gostado desse post e até mais!

sql server clr c# csharp baixar download ler read enviar subir upload arquivos files listar list integrar integrate integração integration servidor ftp server

sql server clr c# csharp baixar download ler read enviar subir upload arquivos files listar list integrar integrate integração integration servidor ftp server