VB .NET - Armazenando documentos em um banco de dados
Digitalizar um documento é gerar um arquivo digital que contém bytes e alguma informação sobre sua estrutura. Atualmente podemos ter uma grande variedade de documentos digitais como arquivos de imagens, documentos de texto, arquivos de áudio,etc.
Os arquivos digitais não são um problema em si mesmo, na verdade eles foram criados para ocupar menos espaço e para serem manipulados mais facilmente.
O grande problema com esses arquivos é o seu armazenamento e a sua localização. Geralmente eles acabam se espalhando de forma caótica pelo ambiente, quer em uma máquina local ou pelos servidores de uma rede, o que acaba trazendo um sério problema de localização e tratamento destes arquivos. (Nem sempre podemos compartilhar as pastas de rede)
Por que não usar então um banco de dados para armazenar os documentos digitais centralizando assim a sua localização ?
Neste arquivo eu vou mostrar como podemos armazenar documentos digitais em um banco de dados SQL Server. Vou usar a versão SQL Server 2008 Express Edition como exemplo.
No caso específico do banco de dados SQL Server temos o tipo de dados IMAGE que é um repositório para um array de bytes e que pode ser usado para armazenar e recuperar qualquer tipo de arquivo. Vamos usar esse tipo de dados em nossa tabela de exemplo.
Para realizar o armazenamento e a recuperação dos arquivos na tabela do banco de dados teremos que fazer a serialização dos mesmos que nada mais é que a transformação do conteúdo de cada arquivo em um array de bytes. Dessa forma facilitamos o transporte e a distribuição dos arquivos em uma rede quer seja local quer seja na internet.
Em nosso exemplo vamos armazenar arquivos de imagem, documentos do Word, arquivos PDF em um banco de dados SQL Server.
Os recursos usados serão:
Definindo o banco de dados e a tabela
Vamos iniciar o Visual Studio Express 2012 for Desktop e usá-lo para criar o banco de dados e a tabela usada no exemplo deste artigo.
No menu VIEW clique em Other Windows -> DataBase Explorer para abrir a DataBase Explorer exibindo as conexões existentes com as fontes de dados:
Obs: Na nova versão você já verá o novo estilo visual adotado pelo Visual Studio com a nova logomarca para refletir o design do Metro;
Na janela DataBase Explorer clique com o botão direito do mouse sobre Data Connections e no menu suspenso clique em Add Connection...
Na janela Add Connection, em Server Name,informe (localdb)\v11.0 e em Select or enter a database name informe o nome Gedoc que será o nome do nosso banco de dados.
Clicando no botão OK será aberta a janela informando que o banco de dados não existe e solicitando a sua confirmação para criá-lo. Clique no botão Sim:
O Microsoft SQL
Server 2012 Express LocalDB é um modo de
execução do SQL Server Express destinado a
desenvolvedores de programas. A instalação do LocalDB copia um conjunto mínimo de arquivos necessários para iniciar o mecanismo de Banco de Dados do SQL Server. Quando o LocalDB é instalado, os desenvolvedores iniciam uma conexão usando uma cadeia de conexão especial. Na conexão, a infraestrutura necessária do SQL Server é criada e iniciada automaticamente, permitindo que o aplicativo use o banco de dados sem tarefas de configuração complexas ou demoradas. O Developer Tools pode
fornecer aos desenvolvedores um mecanismo de Banco de
Dados do SQL Server que permite que eles gravem O SQL Server Express LocalDB deve ser usado em lugar do recurso de instância de usuário do SQL Server Express, que ficou obsoleto. |
Você verá na janela DataBase Explorer a conexão com o banco de dados recém criado. Vamos criar a nossa tabela no banco de dados.
Expanda os objetos da conexão e clique com o botão direito do mouse sobre Tables;
No menu suspenso clique em Add New Table;
Na janela do descritor SQL defina a estrutura da tabela com os seguintes campos:
Se você clicar sobre a conexão com o banco de dados Gedoc poderá ver na janela de propriedades a string de conexão definida:
Criando o projeto no Visual Studio for Desktop
Abra o Visual Studio for Desktop e no menu File clique em New Project e selecione a linguagem Visual Basic e o template Windows Forms Application informando o nome Gedoc;
Será criada uma solução contendo o projeto Windows Forms com um formulário form1.vb e arquivo de configuração App.Config.
Altere o nome do formulário form1.vb criado para frmMenu.vb e a partir da ToolBox inclua um controle MenuStrip no formulário.
O formulário frmMenu será um container MDI e para isso vamos definir a sua propriedade IsMDIContainer como True;
A seguir vamos criar as opções do menu no formulário conforme mostra a figura a seguir:
Na opção Salvar iremos abrir o formulário frmSalvar que permitirá ao usuário selecionar um arquivo e salvar o arquivo no banco de dados.
Na opção Exibir o formulário frmCarregar irá recuperar um documento salvo no banco de dados e conforme o seu formato exibi-lo.
A opção Sobre exibe informações da aplicação via formulário frmSobre.
Vamos criar então os três formulários que serão usados na aplicação.
No menu Project clique em Add New Item e a seguir e selecione o template Windows Forms Application.
Dessa forma crie os formulários : frmSalvar.vb , frmCarregar.vb e frmSobre.vb.
Vamos definir no arquivo App.Config a string de conexão definida para o banco de dados Gedoc conforme o código abaixo:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <configSections> </configSections> <connectionStrings> <add name="ConexaoDigital" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=Gedoc;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration> |
Vamos agora incluir um Módulo em nossa solução. No menu Project clique em Add New Item;
A seguir selecione o template Module informando o nome Geral;
Neste módulo vamos definir 3 métodos e 1 estrutura que serão visíveis em todo projeto. Os métodos serão usados para fazer o tratamento do arquivo realizando a serialização e recuperando os bytes para exibição e a estrutura irá conter 3 propriedades que representam um documento conforme definimos na tabela Documentos:
No módulo Geral defina uma estrutura conforme o código abaixo:
Structure Documento Public Property ID As Integer Public Property Nome As String Public Property Conteudo As Byte() End Structure |
A estrutura documento possui os campos definidos como propriedades para armazenar os dados de um documento. |
A seguir inclua o código dos 3 métodos :
Imports System.IO Imports Gedoc.Documento Module Geral ' Serializa o arquivo retornando array de bytes Public Function GetBytes(ByVal _nomeArquivo As String) As Byte() ' obtém os dados do arquivo Try Using arquivo As New FileStream(_nomeArquivo, FileMode.Open, FileAccess.Read) Dim br As New BinaryReader(arquivo) Dim retorno As Byte() = br.ReadBytes(CInt(arquivo.Length)) Return retorno End Using Catch ex As Exception Throw ex End Try End Function ' Retorna os dados do documento a partir do arquivo informado Public Function GetDocumento(ByVal _nomeArquivo As String) As Documento Try ' executa o método que obtém informações do arquivo Dim documento As Documento = GetDadosArquivo(_nomeArquivo) ' serializa o arquivo documento.Conteudo = GetBytes(_nomeArquivo) Return documento Catch ex As Exception Throw ex End Try End Function Private Function GetDadosArquivo(ByVal _nomeArquivo As String) As Documento Try ' instancia o objeto de retorno, le os dados e retorna Return New Documento() With {.Nome = _nomeArquivo} Catch ex As Exception Throw End Try End Function |
Neste momento nossa solução terá todos itens necessários para o seu funcionamento e na janela Solution Explorer devermos ver a seguinte estrutura:
Definindo o menu de opções
Vamos começar com o formulário frmMenu que exibir as opções da aplicação permitindo ao usuário salvar e recuperar documentos.
Inclua o código abaixo no formulário frmMenu.vb:
Public Class FrmMenu Private Sub SalvarToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles SalvarToolStripMenuItem.Click Dim frmSalvar As New frmSalvar() frmSalvar.MdiParent = Me frmSalvar.Show() End Sub Private Sub ExibirToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ExibirToolStripMenuItem.Click Dim frmExibir As New frmCarregar() frmExibir.MdiParent = Me frmExibir.Show() End Sub Private Sub SobreToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles SobreToolStripMenuItem.Click Dim frmAbout As New frmSobre() frmAbout.MdiParent = Me frmAbout.Show() End Sub End Class |
No código acima estamos criando uma instância do formulário que desejamos exibir e a seguir definimos qual o formulário pai desta instância definindo a propriedade MdiParent como Me. A seguir exibimos o formulário desejado.
Salvando documentos
No formulário frmSalvar.vb inclua os seguintes controles:
Neste formulário defina as declarações dos namespaces que iremos usar :
Imports
System.Data.SqlClient
Imports System.IO
Imports System.Security
Logo após a declaração do formulário vamos definir as propriedades _conexao do tipo SqlConnection e _documento do tipo Documento:
Dim
_conexao As SqlConnection
Dim _documento As Documento
No botão btnlocalizar vamos incluir o código que permite ao usuário selecionar um documento para ser salvo no banco de dados.
Para isso vamos abrir uma janela Open File Dialog a partir da qual o usuário irá selecionar o arquivo:
Private Sub btnLocalizar_Click(sender As Object, e As EventArgs) Handles btnLocalizar.Click 'define as propriedades do controle 'OpenFileDialog Me.ofd1.Multiselect = False Me.ofd1.Title = "Selecionar Documento" ofd1.InitialDirectory = "C:\dados\" 'aplica um filtro ofd1.Filter = "Arquivos (*.PDF;*.AVI;*.MP3;*.BMP;*.JPG;*.GIF,*.PNG,*.TIFF)|*.PDF;*.AVI;*.MP3;*.BMP;*.JPG;*.GIF;*.PNG;*.TIFF|" & "Todos (*.*)|*.*" ofd1.CheckFileExists = True ofd1.CheckPathExists = True ofd1.FilterIndex = 2 ofd1.RestoreDirectory = True Dim dr As DialogResult = Me.ofd1.ShowDialog() If dr = System.Windows.Forms.DialogResult.OK Then If String.IsNullOrEmpty(ofd1.FileName) Then Return End If Try txtDocumento.Text += ofd1.FileName 'define os dados do documento _documento = GetDocumento(ofd1.FileName) _documento.Conteudo = GetBytes(ofd1.FileName) _documento.Nome = txtDocumento.Text btnSalvar.Enabled = True Catch ex As SecurityException ' O usuário não possui permissão para ler arquivos MessageBox.Show((("Erro de segurança Contate o administrador de segurança da rede." & vbLf & vbLf & "Mensagem : ") + ex.Message & vbLf & vbLf & "Detalhes (enviar ao suporte):" & vbLf & vbLf) + ex.StackTrace) Catch ex As Exception ' Não pode carregar a imagem (problemas de permissão) MessageBox.Show(("Não é possível exibir a imagem : " & ofd1.FileName & ". Você pode não ter permissão para ler o arquivo , ou " _ & " ele pode estar corrompido." & vbLf & vbLf & "Erro reportado : ") + ex.Message) End Try End If End Sub |
Após selecionar o arquivo e obter os dados documento o usuário deverá clicar no botão Salvar para armazenar o documento no banco de dados. O código deste botão é visto abaixo:
Private Sub btnSalvar_Click(sender As Object, e As EventArgs) Handles btnSalvar.Click SalvarDocumento() End Sub Public Sub SalvarDocumento() ' Cria o comando SQL Dim commandString As String = "INSERT INTO Documentos (Nome,Conteudo) VALUES (@Nome, @Conteudo)" ' define a string de conexão com o banco de dados _conexao = New SqlConnection("Data Source=(localdb)\v11.0;Initial Catalog=Gedoc;Integrated Security=True") Try 'Abre a conexão com o banco de dados _conexao.Open() Dim command As New SqlCommand(commandString, _conexao) ' adiciona os parâmetros command.Parameters.AddWithValue("Nome", _documento.Nome) command.Parameters.AddWithValue("Conteudo", _documento.Conteudo) ' Grava o documento no banco de dados command.ExecuteNonQuery() MessageBox.Show("Arquivo salvo com sucesso no banco de dados !") btnSalvar.Enabled = False Catch ex As Exception Throw ex Finally _conexao.Close() End Try End Sub |
Recuperando documentos
No formulário frmCarregar.vb iremos recuperar e exibir um documento armazenado no banco de dados.
Para facilitar a vida do usuário vamos exibir o conteúdo da tabela Documentos em um controle DataGridView de forma que o usuário poderá selecionar uma linha do controle e o nome do arquivo será exibido em um TextBox(txt.
Clicando no botão Exibir iremos verificar o formato do arquivo e se o mesmo for um arquivo de imagem iremos exibi-lo em um controle PictureBox e se form de outro formato iremos abrir o documento usando a classe Process do namespace System.Diagnostics.
Usando a classe Process você pode obter informações sobre processos e aplicações , pode também usá-la para criar uma instância de uma aplicação ou parar um processo que esta sendo executado. A grosso modo poderíamos dizer que a classe Process é a sucessora do comando Shell do VB6 (lembra dele ?)
Vamos incluir no formulário frmCarregar os seguintes controles:
Conforme o leiaute abaixo:
Vamos declara os namespaces usados no formulário:
Imports
System.Data.SqlClient
Imports System.IO
Imports System.IO.File
Imports System.Diagnostics
No início do formulário vamos declarar as variáveis para tratar a conexão e o documento:
Dim
_conexao As SqlConnection
Dim _documento As Documento
No evento Load do formulário temos o código que acessa a tabela Documentos e exibe os dados no controle DataGridView:
Private Sub frmCarregar_Load(sender As Object, e As EventArgs) Handles MyBase.Load Try ' cria comando SQL Dim commandString As String = "Select id, nome from Documentos" ' Cria conexao com o banco de dados _conexao = New SqlConnection("Data Source=(localdb)\v11.0;Initial Catalog=Gedoc;Integrated Security=True") Dim command As New SqlCommand(commandString, _conexao) ' Cria um dataAdapter Dim adapter As New SqlDataAdapter(command) ' preenche o DataTable. Dim dataTable As New DataTable() 'exibe os documentos no datagridview adapter.Fill(dataTable) dgvDocumentos.DataSource = dataTable Catch ex As Exception Throw ex Finally _conexao.Close() End Try End Sub |
No evento CellEnter do datagridview temos o código que obtém o nome e o local do documento e o código do documento exibindo-os nos controles do formulário:
Private Sub dgvDocumentos_CellEnter(sender As Object, e As DataGridViewCellEventArgs) Handles dgvDocumentos.CellEnter 'obtem o valor da primeira e segunda colunas da célula selecionada e atribui ao textbox e label no formulario txtDocumento.Text = dgvDocumentos.Rows(e.RowIndex).Cells(1).Value().ToString lblID.Text = dgvDocumentos.Rows(e.RowIndex).Cells(0).Value().ToString End Sub |
No botão Exibir recuperamos o documento e exibimos o seu conteúdo:
Private Sub btnExibir_Click(sender As Object, e As EventArgs) Handles btnExibir.Click Try _documento = New Documento _documento.Conteudo = RecuperarDocumento() 'obtem a extensão do arquivo Dim fInfo As New FileInfo(txtDocumento.Text) Dim extensao As String = fInfo.Extension ' cria um objeto MemoryStream a partir dos dados do documento Dim novoStream As New MemoryStream(_documento.Conteudo) 'define as extensões para imagem Dim Extensoes As New List(Of String)(New String() {".jpg", ".bmp", ".png", ".gif"}) 'verifica se a extensão é de uma formato de imagem e exige a imagem If Extensoes.Contains(extensao) Then picDocumento.Image = System.Drawing.Image.FromStream(novoStream) picDocumento.SizeMode = PictureBoxSizeMode.StretchImage Else Using fs As New FileStream(txtDocumento.Text, FileMode.OpenOrCreate, FileAccess.Write) fs.Write(_documento.Conteudo, 0, _documento.Conteudo.Length) fs.Flush() fs.Close() End Using Process.Start(txtDocumento.Text) End If Catch ex As Exception Throw ex End Try End Sub |
O código que usamos para exibir um documento que não é uma imagem é :
Using fs As
New FileStream(txtDocumento.Text, FileMode.OpenOrCreate,
FileAccess.Write)
fs.Write(_documento.Conteudo, 0,
_documento.Conteudo.Length)
fs.Flush()
fs.Close()
End Using
Process.Start(txtDocumento.Text)
A classe Process inicia o aplicativo associado ao tipo de documento armazenado permitindo que seja exibido os mais diversos tipos de documentos.
A rotina RecuperarDocumento() obtém o conteúdo do documento armazenado como um array de bytes a partir do seu código:
Public Function RecuperarDocumento() As Byte() Try ' cria um comando SQL filtrando pelo numero de codigo (Id) do documento Dim commandString As String = "Select conteudo from Documentos where id = " & Convert.ToInt32(lblID.Text) ' Cria uma conexão com o banco de dados _conexao = New SqlConnection("Data Source=(localdb)\v11.0;Initial Catalog=Gedoc;Integrated Security=True") Dim command As New SqlCommand(commandString, _conexao) ' Cria um data adapter. Dim adapter As New SqlDataAdapter(command) ' preenche um DataTable. Dim dataTable As New DataTable() adapter.Fill(dataTable) ' verifica se existem registros no banco de dados com o criterio If dataTable.Rows.Count > 0 Then ' O documento esta armazenado na forma de bytes na coluna conteudo ' Retorna esses bytes do primeiro registro encontrado para um novo buffer. Dim buffer() As Byte = CType(dataTable.Rows(0)("Conteudo"), Byte()) Return buffer Else MessageBox.Show("Arquivo não localizado!") Return Nothing End If Catch ex As Exception Throw ex Finally _conexao.Close() End Try End Function |
Abaixo vemos o projeto em execução:
O objetivo básico do artigo foi cumprido : mostrei como podemos armazenar e recuperar documentos em um banco de dados.
A forma de implementação usada não esta aderente às boas práticas e precisa ser melhorada. Temos que separar responsabilidades, evitar código duplicado, facilitar a manutenção, etc.
Em outro artigo eu pretendo mostrar como podemos ajustar a aplicação ajustando às boas práticas de programação com aplicação de conceitos básicos da orientação a objetos, sim porque neste estágio estamos longe de ter uma aplicação OOP.
Pegue o projeto completo aqui: Gedoc.zip
Mateus 8: 20 Respondeu-lhe Jesus: As raposas têm covis, e as aves do céu têm ninhos; mas o Filho do homem não tem onde reclinar a cabeça.
João 2:6
“Aquele que diz que permanece nele, esse deve também andar assim como ele andou”1 Pedro 2:11 “Amados, exorto-vos, como peregrinos e forasteiros que sois, a vos absterdes das paixões carnais, que fazem guerra contra a alma.”
Efésios 4:1-2 - “Rogo-vos, pois, eu, o prisioneiro no Senhor, que andeis de modo digno da vocação a que fostes chamados, como toda a humildade e mansidão, com longanimidade, suportando-vos uns aos outros em amor.”
Efésios 5:8 - Pois, outrora, éreis trevas, porém, agora, sois luz no Senhor; andai como filhos da luz.”
Jesus não teve nenhum bem terreno, não procurou ajuntar riqueza nessa terra; devemos andar como ele andou, como peregrinos e forasteiros nesta terra com humildade e mansidão. Muito diferente do que vemos espalhado por ai sendo propalado pelos falsos profetas e homens cheios de ganância que só pensam nas coisas dessa terra e deturpam a palavra da verdade.
Referências: