Database come archivio di file binariQuasi ognuno di voi avrà sicuramente utilizzato un Database nel corso della sua vita da Programmatore. Generalmente però tutti lo utilizziamo per creare delle tabelle con tanti campi in cui andremo inserire Records con tutte le informazioni di cui abbiamo bisogno. Records che possono essere transazioni bancarie, utenti registrati presso un sito, o elenchi in genere.
I Database sono strumenti molto potenti e flessibili che ci permettono di fare tante cose.
Una di queste è la possibilità di inserire direttamente dati in formato binario quindi file eseguibili per esempio, file MP3, file video, PDF, immagini e via di seguito.
Il nostro database quindi può diventare con poca fatica un archivio di file.
Alcune case grosse come
Oracle hanno prodotto addirittura delle estensioni al Database che vi permettono di trasformare il Database Server in un file System e sfruttarne la sua potenza per immagazzinare i file ed effettuare ricerche per esempio. E' un po' l'obbiettivo che ha anche Microsoft cioè di costruire un File System che si appoggia ad un Database (n.d.r. SQL Server) anche se in questo caso è un po' diverso rispetto a quello che fa Oracle con il suo prodotto
Oracle Files.
Definizione di BLOBsAbbiamo fatto una piccola introduzione ma non abbiamo ancora definito questa nuova entità cioè i
BLOB nome che sta per
Binary Large Objects letteralmente oggetti binari larghi o grossi come dimensione, dopo capiremo il significato di "large".
Un campo
BLOB in un database serve quindi per immagazzinare dati Binari generici o specifici come indicato sopra (MP3, PDF, ecc.)
Quasi tutti i Database supportano i
BLOBs, da
Oracle a
SQL Server fino a
Microsoft Access e pure
MySQL.
Pro e Contro dell'utilizzo di campi tipo BLOBNon tutto è oro ciò che luccica. Certamente poter utilizzare i
BLOB è una cosa eccezionale e utile. Pensate per esempio ad un negozio online che può immagazzinare in formato PDF tutte le fatture in formato elettronico emesse ai clienti.
Oppure un sito Web per avere una migliore gestione dei contenuti può immagazzinare tutte le immagini catalogandole per dimensione, tipo e occupazione di spazio.
Dicevamo prima del termine "large". Bene, tanto per fare un esempio e per capire che cosa effettivamente significhi posso dire che una colonna di tipo "image" su un Database SQL Server può contenere fino a circa 2 Giga di dati :-O. Parecchi vero ?
Si può capire con un minimo sforzo che problemi potreste avere a gestire tabelle di questo tipo con dati molto ingombranti. Le dimensioni dei vostri Database possono aumentare sensibilmente e gestire poi una mole di dati di questo tipo può diventare dispendioso in termini di tempo e appesantire il database server stesso. Anche dover fare un backup la cosa diventa complicata. Certo... se avete a disposizione ingenti quantità di denaro per acquistare hardware allora il problema non è così rilevante.
Vi consiglio quindi di pensare intensamente e ragionare prima di andare ad implementare soluzioni di questo tipo (che facciano uso di BLOB) prima che un giorno o l'altro i DB vadano fuori controllo e magari sia necessario ritornare su soluzioni più standard e più facili da gestire.
Gestire BLOBs con .NETNel nostro esempio utilizzeremo le classi
SqlClient di .NET e quindi il Database
SQL Server. Se dovete lavorare con Access invece dovrete utilizzare
OleDb.
Mostreremo negli esempi seguenti come immagazzinare all'interno di un campo BLOB un'immagine a caso presa dal file system e poi effettuare il processo contrario, cioè estrarla dal Database e ripristinarla su disco in modo da renderla utilizzabile per ulteriori elaborazioni o spedirla ai vostri amici.
Come prima operazione creiamo una tabella su SQL Server con una colonna di tipo
image che potrà ospitare i nostri dati, una colonna
ID di tipo
int che conterrà l'indice dell'immagine e una colonna
ImageName di tipo
nvarchar 50 che conterrà il nome del file.
E' possibile fare l'operazione utilizzando l'
Enterprise Manager di SQL oppure anche con il
Query Analyzer per chi conosce bene la sintassi SQL.
Per chi preferisce questo è lo script SQL per creare la tabella:
CREATE TABLE [dbo].[Images] (
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[ImageData] [image] NULL ,
[ImageName] [nvarchar] (50) COLLATE Latin1_General_CI_AS NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
Passiamo ora alla costruzione di una
WinForm e a scrivere il codice necessario per inserire/estrarre l'immagine/i.
La Form creata è la seguente:
E' abbastanza semplice ed è composta da:
- Una
TextBox chiamata
txtFilepath che contiene il Path al file da leggere/scrivere
- Un
Button chiamato
cmdWriteBlob che contiene il codice per immagazzinare l'immagine nel DB
- Un
Button chiamato
cmdReadBlob che contiene il codice per estrapolare l'immagine dal DB
- Una
Picture Box chiamata
PictureBox1 per rappresentare l'immagine
Cominciamo a sottolineare che per far funzionare questo progetto è necessario referenziare i seguenti due namespaces:
using System.IO; //per l'accesso ai file su disco
using System.Data.SqlClient; //per l'accesso al Database SQL Server
Scrittura del BLOB nel DatabaseCome vedete la tabella che abbiamo creato è ancora vuota:
Fate un doppioclick sul button
cmdWriteBlob e andiamo a digitare il seguente codice che ora spieghiamo in dettaglio:
//Apertura Connessione al Database
SqlConnection sqlconn=new SqlConnection("Server=Localhost;Initial Catalog=DBImmagini;UID=sa;PWD=sa");
sqlconn.Open();
//Legge i dati del file indicato nella casella di testo in memoria in un buffer
FileStream fs=new FileStream(txtFilepath.Text,FileMode.Open,FileAccess.Read);
byte[] dati = new byte[fs.Length];
fs.Read(dati,0,(int)fs.Length);
fs.Close();
//Crea un Command per l'inserimento nella Tabella
SqlCommand cmd=new SqlCommand("INSERT INTO Images (ImageData,ImageName) VALUES (@ImageData,@ImageName)",sqlconn);
//Aggiunge dei parametri al Command specificando i valori da inserire
SqlParameter par=cmd.Parameters.Add("@ImageData",SqlDbType.Image);
par.Value=dati;
par=cmd.Parameters.Add("@ImageName",SqlDbType.NVarChar,50);
par.Value=Path.GetFileName(txtFilepath.Text);
//Esegue l'inserimento
cmd.ExecuteNonQuery();
//Chiude la connessione
sqlconn.Close();
Come vedete i passi fondamentali sono quattro:
1) Apertura connessione al DB
2) Lettura del file da disco e riempimento del Buffer
3) Creazione di un SqlCommand e di SqlParameter specificando i dati
4) Eseguire il SqlCommand e chiudere la connessione al DB
Le parti forse meno comprensibili sono la 2) e la 3).
Utilizziamo la classe
FileStream per aprire e leggere il file specificato nella textbox. Nel mio caso era c:\shot.png che è un file che dovete creare appositamente o se ne avete un altro anche di diverso tipo è uguale.
Viene poi creato un buffer, cioè un'area temporanea di memoria ove immagazzinare il file dopo la lettura:
byte[] dati = new byte[fs.Length];Come vedete viene passato come parametro fs.Length che non è altro che la dimensione del file. Allochiamo quindi una sequenza di cellette (bytes) quanti sono i bytes del file in modo da contenerlo completamente.
In seguito, tramite la Read leggiamo il contenuto e lo immettiamo nel buffer appena creato:
fs.Read(dati,0,(int)fs.Length);A questo punto è possibile creare un
SqlCommand che avrà la Query di inserimento. Come vedete ci sono due parametri che cominciano con
@. Successivamente infatti creiamo i due parametri e assegnamo loro il dato nel primo caso il nostro buffer di dati, nel secondo il nome del file (shot.png).
SqlParameter par=cmd.Parameters.Add("@ImageData",SqlDbType.Image);
par.Value=dati;Ora potete lanciare il programma e premere il pulsante, questo è il risultato:
In questo momento il nostro file è immagazzinato nella tabella. I dati effettivi binari del file sono nel nostro campo BLOB che è contrassegnato dalla parola
"Binary" che sta a significare, che lì ci sono dati binari. L'ID si incrementa automaticamente ed infatti parte da 1.
Lettura del BLOB dal Database e scrittura su discoIl processo per leggere il file dal Database e scriverlo su disco è praticamente l'opposto. In questo caso faremo uso di un
DataSet e di un
DataAdapter che servirà per riempirlo.
//Apertura Connessione al Database
SqlConnection sqlconn=new SqlConnection("Server=Localhost;Initial Catalog=DBImages;UID=sa;PWD=sa");
sqlconn.Open();
//Creazione di DataAdapter e riempimento del DataSet
SqlDataAdapter da=new SqlDataAdapter("SELECT * FROM IMAGES",sqlconn);
DataSet ds=new DataSet();
da.Fill(ds);
//Allocazione di un Buffer per contenere i dati binari provenienti dal Database
byte[] dati = new byte[0];
dati=(byte[])ds.Tables[0].Rows[0]["ImageData"];
//Scrittura dei dati su disco
FileStream fs=new FileStream("C:\\" + ds.Tables[0].Rows[0]["ImageName"].ToString(),FileMode.Create,FileAccess.Write);
fs.Write(dati,0,dati.Length);
fs.Close();
//Chiusura connessione
sqlconn.Close();
A differenza della prima routine questa è anche più semplice.
Dopo aver aperto la connessione, utilizziamo un
DataAdapter per recuperare tutti i dati dalla nostra tabella (SELECT * FROM Images). Facciamo un
Fill su un
DataSet appena creato ed otteniamo i nostri dati. (La connessione adesso si potrebbe anche chiudere, vista la natura di Database "disconnesso" del DataSet).
Viene creato anche in questo caso un Array di Byte vuoto (dimensione 0) a cui assegnamo in seguito il campo
ImageData del Record con indice 0 del nostro DataSet cioè i dati della nostra Immagine.
Creiamo un oggetto FileStream per la scrittura su disco, avente per nome il contenuto della seconda colonna del nostro Database (
ImageName) ossia il nome originale del file, e infine con il metodo
Write scriviamo su disco i dati. Non ci resta che chiudere il FileStream e la connessione a DB.
Lanciate il programma premete
ReadBlob e come per magia vi ritroverete sul disco il file ricostruito
shot.png.
Visualizzare sulla WinForm il BLOB appena lettoLa ciliegina sulla torta. Per chi non volesse scrivere su disco i file ma volesse semplicemente visualizzarli si può fare un'integrazione del codice ed usare un
MemoryStream cioè una classe apposita che consente di memorizzare i dati letti in memoria invece che su disco.
Ecco il codice necessario:
//Crea un MemoryStream partendo dal nostro Buffer dati
MemoryStream ms=new MemoryStream(dati,0,dati.Length);
//Crea un'immagine usando come sorgente il MemoryStream
Image i=Image.FromStream(ms);
//Rappresenta l'immagine sulla PictureBox
pictureBox1.Image=i;
Ed ecco il risultato:
In allegato all'articolo c'è il progetto in linguaggio
C#. Chi stesse usando
Visual Basic .NET non troverà troppe difficoltà nel fare la conversione.
ConclusioniCome al solito dei brevi e concisi esempi che vi permettono di capire le potenzialità offerte in questo caso da SQL Server (ma anche altri database le offrono) e dalle classi del .NET Framework che sono utili in quasi ogni scenario.
Conoscendo i
BLOBs ora potete espandere e integrare le vostre applicazioni creando archivi e/o cataloghi di dati in modo da gestire più semplicemente e rapidamente le vostre informazioni testuali (Records classici di solo testo) e anche multimediali (mp3, immagini, altro). Un occhio di riguardo alle dimensioni dei file inseriti e alle prestazioni globali del Database. Buon divertimento!