O MS SQL Server fornece 2 tipos de BLOBs : binary e varbinary. A diferença entre ambos é que no binary o tamanho é fixo; o tamanho estipulado para o campo é o mesmo em todos os registros da tabela, o que o torna apropriado para armazenar estruturas cujo tamanho é conhecido e pré fixado. Já o varbinary aceita dados de tamanho variável, isto é, o campo pode ter tamanhos diferentes em cada registro na tabela. É útil para quando não se sabe de antemão qual o tamanho da informação a ser armazenada, como por exemplo, uma imagem, um vídeo, um áudio ou outro arquivo qualquer. Observação: de acordo com o MSDN, o tipo IMAGE do SQL Server será descontinuado. Veja aqui a informação a esse respeito.
O Oracle oferece o tipo BLOB, equivalente ao varbinary. Há uma tabela aqui com os tipos de dados do Oracle e o tamanho de cada um em várias versões deste banco de dados.
Suponha que você tem um texto formatado do tipo RTF (Rich Text Format) representando um contrato qualquer. Um usuário pode editar o conteúdo através de um componente TRichEdit inserido numa tela feita em Delphi ou C++ Builder e armazenar as alterações numa tabela no banco de dados. Para mostrar aqui como fazer isso, vou criar a seguinte tabela.
-- Em SQL Server
create table INFO
(
ID int not null,
DESCRICAO varchar(255) null,
OBJETOS varbinary(max) null
)
create table INFO
(
ID int not null,
DESCRICAO varchar(255) null,
OBJETOS varbinary(max) null
)
Não inclui chave primária por questão de simplicidade mas você deveria fazer isso num projeto real. A expressão max na declaração do BLOB indica que eu não sei qual é o tamanho máximo do conteúdo e que o Banco deve estar preparado para receber mais que os 8K normais.
Se você já tem um RTF pronto em um arquivo externo e quer carregar o contéudo no TRichEdit, pode usar o comando LoadFromFile, deixando o usuário livre para modificar o conteúdo na tela. Vou usar exemplos em C++ mas para Delphi a sequência de comandos é a mesma, bastando ajustar a sintaxe para essa linguagem.
RichEdit1->Lines->LoadFromFile (_FileName);
Inclui no Form também um componente TADOConnection configurado para se conectar ao meu banco de dados e um componente TADOCommand com o comando de inserção na tabela:
INSERT INTO INFO (ID, DESCRICAO, OBJETOS)
VALUES (:ID, :DESCRICAO, :OBJETOS)
VALUES (:ID, :DESCRICAO, :OBJETOS)
A sintaxe :ID sinaliza ao Delphi (ou C++ Builder) que crie um parâmetro com o nome ID para facilitar o acesso. Ao solicitar a gravação, então, pode ser usada a sequência de comandos abaixo:
TMemoryStream *stream = new TMemoryStream ();
// Põe em stream com o conteúdo do RichEdit,
// isto é, o texto do contrato
RichEdit1->Lines->SaveToStream (stream);
// Monta os parâmetros do comando
TParameters *params;
params = ADOCommand1->Parameters;
params->ParamByName("ID")->Value = 1;
params->ParamByName("DESCRICAO")->Value = "Descrição";
params->ParamByName("OBJETOS")->LoadFromStream (stream, ftVarBytes);
// Executa o comando de inserção
ADOCommand1->Execute ();
delete stream;
// Põe em stream com o conteúdo do RichEdit,
// isto é, o texto do contrato
RichEdit1->Lines->SaveToStream (stream);
// Monta os parâmetros do comando
TParameters *params;
params = ADOCommand1->Parameters;
params->ParamByName("ID")->Value = 1;
params->ParamByName("DESCRICAO")->Value = "Descrição";
params->ParamByName("OBJETOS")->LoadFromStream (stream, ftVarBytes);
// Executa o comando de inserção
ADOCommand1->Execute ();
delete stream;
Embora não mostrado, esse trecho de código deve ser inserido numa transação do Banco de Dados. O conteúdo para o BLOB é conseguido através de streams, primeiro salvando o texto formatado do RichEdit para dentro de um stream e depois carregando esse stream para o parâmetro do tipo BLOB do meu comando de inserção. Os demais valores eu deixei fixo apenas para simplificar; numa aplicação real, estas informações também deveriam ser variáveis.
Ficou faltando apenas mostrar como ler o conteúdo a partir do banco. Montei um TADOQuery que faz um SELECT para recuperar o registro da tabela INFO que tem o ID igual a 1. Então, uso esse SELECT para obter o BLOB:
ADOQuery1->Active = true;
if (ADOQuery1->Eof == false)
{
TBlobField *field;
TMemoryStream *stream = new TMemoryStream ();
// Salva o blob no stream
field = (TBlobField *)ADOQuery1->FieldByName ("OBJETOS");
field->SaveToStream (stream);
stream->Position = 0;
RichEdit1->Lines->LoadFromStream (stream);
delete stream;
}
if (ADOQuery1->Eof == false)
{
TBlobField *field;
TMemoryStream *stream = new TMemoryStream ();
// Salva o blob no stream
field = (TBlobField *)ADOQuery1->FieldByName ("OBJETOS");
field->SaveToStream (stream);
stream->Position = 0;
RichEdit1->Lines->LoadFromStream (stream);
delete stream;
}
Ou seja, leio o valor do campo BLOB num stream e alimento de volta o RichEdit, tomando o cuidado de reposicionar o stream no começo já que o comando LoadFromStream carrega a partir da posição atual.
Embora o exemplo tenha sido com um texto, a sequência de comandos seria a mesma para trabalhar com utro tipo qualquer de conteúdo - uma imagem, vídeo, XML, etc.