André Alves de Lima

Talking about Software Development and more…

Salvando arquivos no banco de dados com C# e ADO.NET

Uma das primeiras coisas que aprendemos depois de dominarmos o básico de uma linguagem de programação é como fazemos para manipularmos bancos de dados com essa linguagem. Afinal de contas, temos que salvar os dados da nossa aplicação em algum lugar, não é mesmo? Esse lugar costuma ser um banco de dados.

Com o ADO.NET nós podemos executar sentenças SQL nos mais diversos bancos de dados. Basta termos instalado o provider específico do banco de dados e pronto, tudo deve funcionar sem problema algum. Uma vez que aprendemos a estrutura básica das classes do ADO.NET, nós já conseguimos fazer o CRUD das nossas aplicações e todo mundo fica contente. Isso é, até que chega a hora em que temos que salvar um arquivo no banco de dados. Aí bate aquele desespero: como é que passamos um arquivo na sentença SQL?

Pois bem, nós não passamos! Basta utilizarmos a funcionalidade de parâmetros do ADO.NET, passando a representação binária do arquivo em um parâmetro do comando. Com isso o ADO.NET vai se virar para mandar o arquivo para o banco de dados, sem que tenhamos que ficar fazendo malabarismos com a sentença SQL (para mais informações sobre parâmetros no ADO.NET, confira este artigo).

No vídeo de hoje eu vou mostrar para você como salvar (e recuperar) arquivos no banco de dados com C# e ADO.NET puro. Caso você esteja trabalhando com Entity Framework na sua aplicação, confira este outro artigo onde eu mostro o equivalente para o Entity Framework.

O banco de dados

A princípio, vamos começar com um banco de dados do SQL Server extremamente simples. Ele terá somente uma tabela onde armazenaremos os arquivos. Nessa tabela, teremos um ID auto incremento (chave primária), uma coluna para armazenarmos o nome do arquivo e outra coluna para armazenarmos a representação binária dele:

CREATE TABLE Arquivos (
	ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
	NomeArquivo VARCHAR(255) NULL,
	Arquivo IMAGE NULL)

Interface do usuário

No exemplo desse vídeo, iremos trabalhar com um projeto do tipo “Windows Forms Application“. Porém, os mesmos conceitos poderiam ser totalmente aplicados em outros tipos de projetos, como WPF, Web Forms, MVC, etc.

Na interface do usuário teremos um grid (“dgvArquivos” – com duas colunas “ID” e “NomeArquivo“) e dois botões (“btSalvar” e “btAbrir“):

Carregando o grid

A primeira coisa que temos que fazer é carregarmos o grid. Separaremos o código de abertura de conexão em um método próprio porque ele será utilizado em várias partes do exemplo. Além disso, dessa forma fica mais fácil substituir o banco de dados mais para frente:

        public FormArquivos()
        {
            InitializeComponent();

            CarregarGrid();
        }
        private void CarregarGrid()
        {
            try
            {
                using (var conn = AbrirConexao())
                {
                    conn.Open();
                    using (var comm = conn.CreateCommand())
                    {
                        comm.CommandText = "SELECT ID, NomeArquivo FROM Arquivos";
                        var reader = comm.ExecuteReader();
                        var table = new DataTable();
                        table.Load(reader);
                        dgvArquivos.DataSource = table;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        private IDbConnection AbrirConexao()
        {
            return new SqlConnection(@"Data Source=.\sqlexpress;Initial Catalog=Testes;Integrated Security=True");
        }

É importante ressaltar que, em uma aplicação “de verdade“, o ideal seria separar o código em camadas distintas (acesso a dados, lógica de negócio, interface do usuário, etc). Além disso, a string de conexão deveria estar armazenada em um arquivo de configuração, e não digitada diretamente no código. Nesse exemplo nós estamos fazendo tudo dentro do formulário para não complicarmos muito o exemplo e focarmos no tema do artigo.

Salvando o arquivo

Para salvarmos o arquivo, temos que executar um comando de “INSERT” no banco. Porém, como é que fazemos para passar a representação binária do arquivo no comando “INSERT“? É aí que mora todo o segredo: nós não passamos! Ao trabalharmos com a funcionalidade de parâmetros do ADO.NET, nós só precisamos passar o array de bytes com o conteúdo do arquivo em um parâmetro do comando. Aí o próprio ADO.NET já se vira para mandar essa informação corretamente para o banco de dados, independente do banco que estivermos utilizando:

        private void btSalvar_Click(object sender, EventArgs e)
        {
            try
            {
                var arquivo = EscolherArquivo();

                if (!string.IsNullOrWhiteSpace(arquivo))
                {
                    SalvarArquivo(arquivo);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        private string EscolherArquivo()
        {
            var retorno = string.Empty;

            using (var dialog = new OpenFileDialog())
            {
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    retorno = dialog.FileName;
                }
            }

            return retorno;
        }
        private void SalvarArquivo(string arquivo)
        {
            using (var conn = AbrirConexao())
            {
                conn.Open();
                using (var comm = conn.CreateCommand())
                {
                    comm.CommandText = "INSERT INTO Arquivos (NomeArquivo, Arquivo) VALUES (@NomeArquivo, @Arquivo)";
                    ConfigurarParametrosSalvar(comm, arquivo);
                    comm.ExecuteNonQuery();
                    CarregarGrid();
                }
            }
        }
        private void ConfigurarParametrosSalvar(IDbCommand comm, string arquivo)
        {
            comm.Parameters.Add(new SqlParameter("NomeArquivo", Path.GetFileName(arquivo)));
            comm.Parameters.Add(new SqlParameter("Arquivo", File.ReadAllBytes(arquivo)));
        }

Abrindo o arquivo

Para recuperarmos o arquivo do banco de dados, nós criamos um comando com um “SELECT” passando como parâmetro o “ID” da linha selecionada no grid. Em seguida, nós chamamos o método “ExecuteScalar“, que retornará o array de bytes com o arquivo que estava armazenado no banco.

Uma vez tendo o array de bytes em mãos, você pode fazer o que quiser com ele. No exemplo do vídeo, nós salvamos o arquivo na pasta “TEMP” e utilizamos a classe “Process” para abrirmos o arquivo com a aplicação padrão da sua extensão:

        private void ConfigurarParametrosAbrir(IDbCommand comm)
        {
            comm.Parameters.Add(new SqlParameter("ID", dgvArquivos.CurrentRow.Cells["ID"].Value));
        }
        private void btAbrir_Click(object sender, EventArgs e)
        {
            try
            {
                using (var conn = AbrirConexao())
                {
                    conn.Open();
                    using (var comm = conn.CreateCommand())
                    {
                        comm.CommandText = "SELECT Arquivo FROM Arquivos WHERE (ID = @ID)";
                        ConfigurarParametrosAbrir(comm);
                        var bytes = comm.ExecuteScalar() as byte[];
                        if (bytes != null)
                        {
                            var nomeArquivo = dgvArquivos.CurrentRow.Cells["NomeArquivo"].Value.ToString();
                            var arquivoTemp = Path.GetTempFileName();
                            arquivoTemp = Path.ChangeExtension(arquivoTemp, Path.GetExtension(nomeArquivo));
                            File.WriteAllBytes(arquivoTemp, bytes);
                            Process.Start(arquivoTemp);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

Trocando o banco de dados

Como separamos o código da abertura do banco e criação dos seus parâmetros, nós podemos facilmente trocar de banco de dados alterando o tipo do objeto de conexão e parâmetro. Por exemplo, para utilizarmos um banco SQLite ao invés do SQL Server, nós poderíamos trocar de SqlConnection para SQLiteConnection e de SqlParameter para SQLiteParameter:

        private IDbConnection AbrirConexao()
        {
            return new SQLiteConnection(@"Data Source=db.db");
        }
        private void ConfigurarParametrosSalvar(IDbCommand comm, string arquivo)
        {
            comm.Parameters.Add(new SQLiteParameter("NomeArquivo", Path.GetFileName(arquivo)));
            comm.Parameters.Add(new SQLiteParameter("Arquivo", File.ReadAllBytes(arquivo)));
        }
        private void ConfigurarParametrosAbrir(IDbCommand comm)
        {
            comm.Parameters.Add(new SQLiteParameter("ID", dgvArquivos.CurrentRow.Cells["ID"].Value));
        }

É claro que esse código só funcionará se adicionarmos a referência às dlls do SQLite através do NuGet e se tivermos o arquivo “db.db” no diretório da aplicação (você pode baixa-lo aqui).

Pode ser que outros bancos de dados implementem a funcionalidade de parâmetros de maneira diferente. Por exemplo, o Microsoft Access não suporta parâmetros nomeados (teríamos que colocar um ponto de interrogação no lugar do nome dos parâmetros e passa-los na mesma ordem que foram definidos na sentença SQL).

Baixe o projeto de exemplo

Para baixar o projeto de exemplo desse artigo, assine a minha newsletter. Ao fazer isso, além de ter acesso ao projeto, 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 no final do artigo.

Até a próxima!

André Lima

Photo by Peter Shanks used under Creative Commons
https://pixabay.com/en/startup-start-up-notebooks-creative-593327/

Song Rocket Power Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/

Newsletter do André Lima

* indicates required



Powered by MailChimp

8 thoughts on “Salvando arquivos no banco de dados com C# e ADO.NET

  • fernando disse:

    Muito bom! Prof André de parabéns! Sabe uma adição que seria interessante e que eu não resolvi ainda? faço esse processo de bytearray com as imagens que pego da câmera, que fiz pelo seu outro tutorial, e exporto isso pro excel com os dados dos clientes…mas as imagens vão como bytearray pro excel, como eu faria pra que elas fossem como imagem pra lá? Grato.

    • andrealveslima disse:

      Olá Fernando, muito obrigado pelo comentário!

      Isso depende de como você está fazendo a geração do arquivo Excel.. Por exemplo, se você estiver gerando o arquivo Excel utilizando Office Automation, você poderia salvar a imagem em um arquivo temporário e adicioná-la através do método Shapes.AddPicture.. Tem um exemplo nesta thread do StackOverflow:

      How to insert a picture in to Excel from C# app?

      Abraço!
      André Lima

  • Tadeu Botelho disse:

    Parabéns André!
    Suas publicações estão cada vez melhores.
    Sucesso para você!
    Abraços

    • andrealveslima disse:

      Olá Tadeu! Muito obrigado pelo comentário! Fico extremamente contente por você estar gostando das publicações.. :)

      Abraço e sucesso aí pra você também!
      André Lima

  • Gilberto Otsuka disse:

    Olá André, primeiramente gostaria de parabeniza-lo por suas publicações e pelo site. Bem didático, claro e objetivo.
    Já sou assinante da newsletter, porém não consegui encontrar o link para download do seu exemplo. Uma sugestão de matéria: Como realizar a Importação de dados do Excel para o SqlServer.
    Um grande abraço e parabéns!

    • andrealveslima disse:

      Olá Gilberto, muito obrigado pelo comentário!

      Vou enviar no seu e-mail o link onde você consegue baixar os projetos de exemplo.. E obrigado pela sugestão, vou colocá-la aqui na minha lista.. :)

      Abraço!
      André Lima

  • José Eduardo disse:

    Boa tarde,

    Também não encontrei o link para baixar o projeto.

Deixe uma resposta

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