Gravando Arquivos Binários no PostgreSQL
por Eduardo Lundgren (braeker)

O armazenamento de arquivos em banco de dados possui vantagens em relação à organização já que tudo estaria armazenado no banco. A integridade e segurança também estariam sendo garantidas pelo SGBD, porém existem as desvantagens. Confira neste artigo quando dou todos os detalhes de como gravar



O armazenamento de arquivos em banco de dados Objeto-Relacional possui vantagens em relação à organização já que tudo estaria armazenado no banco. A integridade e segurança também estariam sendo garantidas pelo SGBD, porém existem as desvantagens. É o caso de precisar do seu código para visualização do arquivo. Caso você tenha armazenado uma imagem no banco, para visualizar você teria que utilizar um script que permitisse a interpretação do binário no browser, por exemplo.

Imagine um site que possui arquivos restritos a certo grupo de usuários. Se estes arquivos estivessem armazenados diretamente no HD do servidor juntamente com os códigos html e php, você poderia utilizar um cliente FTP ou até mesmo a barra de endereço do browser para ter acesso a eles, isso se você conhecer o diretório onde os documentos se encontram. Porém com os arquivos armazenados no Banco isso não seria possível, pois o usuário precisaria de um script que acessasse a base de dados e lhe fornecesse o binário deste arquivo.

Vou utilizar no exemplo o banco de dados PostgreSQL por ser um banco Objeto-Relacional, além de ser o mais avançado banco de dados de código livre disponível.

Existem algumas maneiras de inserir códigos binários de arquivos em banco de dados não relacionais. No MySQL uma solução é o campo BLOB (Binary Large Object), porém você tem que utilizar alguns artifícios para o armazenamento. Há caracteres que são “rejeitados” por esse tipo de campo, se for inserido o binário sem que esses caracteres sejam escapados, provavelmente a integridade do arquivo será afetada.

Uma solução encontrada por mim, não sei se é a mais correta, mas foi converter pra uma tabela de criptografia mais simples, como a Base64, que transforma qualquer caractere de 8 bits num sub-grupo de 6 bits presentes na maioria das tabelas de codificação. Isso reduz a quantidade de caracteres possíveis para [A-za-z0-9+/=] que é facilmente entendido pelo campo BLOB, mas o arquivo fica salvo com um tamanho 33% maior do que numa tabela normal de 8-bits.

[Nota do Editor: O comando correto para se usar com o MySQL é o addslashes()]

No PostgreSQL também é possível usar um campo semelhante ao BLOB o ByteA. A vantagem em relação ao uso do MySQL é que para o postgresql o php possui uma função chamada pg_escape_bytea(), esta função exige PostgreSQL 7.2 ou superior, que retorna valores de byte octais prefixados por \ (ex.: \032). Os usuários devem converter de volta para binários quando quiserem recuperar os dados com o comando pg_unescape_bytea(). Desconheço se o MySQL possui um comando semelhando a este.

Vamos agora mostrar a maneira de se armazenar arquivos como objetos relacionais no PostgreSQL.


Como inserir a imagem no banco?

Imagine um Banco com a seguinte estrutura:

Tabela arquivo:
- nome_foto (Campo String)
- foto_oid (Campo OID)

O raciocínio básico de como inserir uma imagem no banco de dados é bastante simples. Primeiro você tem que abrir uma conexão com o banco de dados, em seguida dizer à essa conexão que será criado um objeto nela. É nesse objeto que será inserido os dados binários do arquivo. Na tabela “arquivo” serão inseridos apenas o nome da foto como string e a referencia ao objeto, esta referencia nada mais é do que um número identificador que aponta para objeto.

O código PHP abaixo explica passo a passo.


<?php
// Conectando ao Banco de Dados
$conexao pg_connect("host=localhost port=5432 user=postgres dbname=teste");

// Abrindo o arquivo em modo de leitura e carregando o 
// binário em uma variável, no Windows é necessário abrir 
// com o modo "rb" no linux só precisa do modo "r".
$arquivo fopen("imagem.jpeg","rb");
$dados fread($arquivo,filesize("imagem.jpeg"));
fclose($arquivo);

// Note que será feita uma Transação ACID 
// (Atômica, Consistente, Isolada e Durável) com o banco, 
// ou seja, tudo ou nada. Uma transação inicia-se com uma 
// Query passando o comando BEGIN e finaliza-se com 
// um comando COMMIT.

// Iniciando Transação com o banco
pg_query($conexao"BEGIN");

// Agora tem que dizer pra conexão aberta ($conexao) que 
// será criado um objeto nela e referenciado pra uma 
// variável chamada de $oid, ou seja, a variavel $oid 
// recebe apenas um numero que é o caminho para o 
// objeto. (Ex: $oid = 12345;)

// Criando o Objeto na conexao $conexao.
$oid pg_lo_create($conexao);

// O que será inserido no campo "foto" do banco não é o 
// binário do arquivo e sim a referencia para o objeto.
// Imagine que o objeto foi criado na seção 12345 do 
// engenho interno. Só será salvo no campo apenas o 
// numero 12345 que referencia o objeto.

// Inserindo referencia ($oid) do arquivo no banco.
pg_query($conexao,"INSERT INTO arquivo (nome_foto,foto_oid) values('imagem.jpeg',$oid)");


// Agora o objeto tem que ser aberto com o modo de 
// escrita para poder ser inserido os dados binarios 
// do mesmo, que foi carregado usando-se um fread().

// Abrindo o objeto
$objeto pg_lo_open($conexao,$oid,"w");

// Inserindo Dados no arquivo
pg_lo_write($objeto,$dados);

// Fechando a conexao com o objeto
pg_lo_close($objeto);

// Finalizando Transação
pg_query($conexao"COMMIT");
?>




Como visuazar a imagem ou arquivo?

O código comentado segue abaixo.


<?php
// Conectando ao Banco de Dados
$conexao pg_connect("host=localhost port=5432 user=postgres dbname=teste");

// Pegando informações sobre a imagem que foi salva no banco
// Esta consulta não retornará o dado binário da imagem 
// juntamente com outras informações da tabela, mas sim 
// a referencia (o identificador) do objeto. Imagine que 
// o código da imagem na tabela seja 7.
$consulta pg_query($conexao,"SELECT * FROM arquivo where codigo=7");
$resultado pg_fetch_array($consulta);

// Seta o identificador do objeto que veio do campo foto no banco.
$identificador $resultado["foto_oid"];

// Iniciando Transação para carregar o arquivo
pg_query($conexao"BEGIN");

// Abrindo o objeto no modo leitura "r" passando como 
// parâmetro o OID, ou seja, o identificador do objeto.
$objeto pg_lo_open($conexao,$identificador,"r");

// Setando Cabeçalho do browser para interpretar que o 
// binário que será carregado é de uma foto do tipo JPEG.
header("Content-Type: image/jpeg");

// Lendo binário da foto passando como referencia a 
// conexão com o objeto ($objeto).
pg_lo_read_all($objeto);

// Fechando Objeto que foi aberto para leitura
pg_lo_close($objeto);

// Finalizando Transação
pg_query($conexao"COMMIT");
?>




Como deletar o objeto?

O código comentado segue abaixo.


<?php
// Conectando ao Banco de Dados
$conexao pg_connect("host=localhost port=5432 user=postgres dbname=teste");

// Pegando informações sobre a imagem que foi salva no banco
// Esta consulta não retornará o dado binário da imagem 
// juntamente com outras informações da tabela, mas sim a 
// referencia (o identificador) do objeto. Imagine que o 
// código da imagem na tabela seja 7.
$consulta pg_query($conexao,"SELECT * FROM arquivo where codigo=7");
$resultado pg_fetch_array($consulta);

// Seta o identificador (oid) do objeto que veio do campo foto no banco.
$identificador $resultado["foto_oid"];

// Iniciando Transação para deletar o arquivo
pg_query($conexao"BEGIN");

// Quando você deletar o registro da tabela, se ela possuir 
// um campo oid o objeto relacionado a ela será apagado 
// simultaneamente, não precisando dar o pg_lo_unlink();

// Deletando registro da tabela
pg_query(“delete from arquivo where codigo=7”);

// Finalizando transação
pg_query($conexao"COMMIT");

// Fechando o objeto
pg_lo_close($objeto);

// Fechando conexão
pg_close();
?>


Obs: Caso você deseje apagar diretamente o objeto use o pg_lo_unlink(), porém se torna desnecessário porque ao apagar o registro da tabela o objeto também é apagado.

// Deletando objeto. Passe como parâmetro a conexão e o identificador (oid) do objeto:


<?php
pg_lo_unlink 
($conexao$identificador);
?>


Este é o primeiro artigo da minha vida e tentei ser o mais claro possível. Espero que tenham gostado! Qualquer duvida, chingamento ou sugestão enviem um comentário abaixo!

Abraços,
Eduardo Lundgren