La memorizzazione di file in un database può essere di importanza cruciale in certi tipi di applicazioni. Si pensi ad esempio ad uno negozio online di musica, il cui database potrebbe contenere file musicali, oppure ad un social network che permetta di pubblicare video, da riprodurre in streaming.
Ci sono due modi per memorizzare file in MongoDB:
- direttamente dentro un documento, utilizzando il tipo BinData (massimo 16 MB);
- in GridFS, un file system progettato appositamente per MongoDB, ottimizzato per grandi dimensioni.
La prima soluzione è raccomandabile quando, oltre ad essere di ridotte dimensioni, i files devono essere acceduti interamente, quindi non quando si intende fare streaming video, per esempio. Magari può essere utile per memorizzare documenti PDF di ridotte dimensioni, o fogli elettronici. GridFS, invece, permette di accedere ai file "a pezzi", ciascuno di dimensioni prefissate (i cosiddetti chunk). Vediamo entrambe queste opzioni più in dettaglio.
Utilizzo di tipi di dato BinData
Possiamo inglobare un file all’interno di un documento in modo molto semplice:
db.docs.insert( {
title: 'MongoDb Tutorial',
size: 20,
data: new BinData(0, 'UXVlc3RhIMOoIHVuYSBndWlkYSBhIE1vbmdvREI=');
});
In questo esempio abbiamo inserito un documento con alcuni campi (title
e size
), a corredo di un campo data
di tipo BinData
che contiene il documento. In questo esempio si può vedere anche il modo di dichiarare un valore BinData
: il primo parametro rappresenta il tipo di dato, mentre il secondo è il contenuto del file codificato in Base64. I driver per i vari linguaggi codificano automaticamente questo dato. Il tipo di dato 0
è il più comune e rappresenta un generico dato binario.
Come anticipato in precedenza i dati inseriti in questo modo possono essere recuperati solo integralmente e non a pezzi, per cui è necessario prestare attenzione alla quantità di dati che si vanno poi a recuperare con le query, perché potrebbero essere di notevoli dimensioni.
Utilizzo di GridFS
GridFS è un sistema realizzato per memorizzare file di grandi dimensioni dentro MongoDB. Quando si memorizza un file tramite GridFS, MongoDB lo suddivide in parti non più grandi di 255K, dette chunk, e li memorizza in una collezione denominata chunks; contemporaneamente, inserisce nella collezione files i metadati del singolo file.
Utilizzare GridFS senza utilizzare un driver (per un'introduzione ai driver di MongoDB, si veda l'apposita lezione di questa guida), MongoDB fornisce lo strumento mongofiles. Si tratta di un programma a riga di comando che permette di interagire direttamente con un’istanza di MongoDB per archiviare, cercare ed eliminare file da GridFS. Vediamo subito un esempio.
Utilizzo di mongofiles per archiviare files
Supponiamo di avere un file video denominato mongodb_tutorial.mpg da archivare su GridFS. Lanciamo mongofiles dalla shell con la seguente riga di comando:
mongofiles -d videos put mongodb_tutorial.mpg
La parola chiave put indica che vogliamo fare un upload del file indicato subito dopo nella riga di comando. L’opzione -d, invece, specifica in quale database vogliamo caricare il file. Se il database indicato non esiste, esso verrà creato.
La risposta del server sarà simile a questa:
connected to: 127.0.0.1
added file: {
_id: ObjectId('54c252b52dcff524dd84a559'),
filename: "mongodb_tutorial.mpg",
chunkSize: 261120,
uploadDate: new Date(1122021301248),
md5: "1d22309005471d80f723ef4b3600f528", length: 11258112
}
La prima riga indica a quale istanza di MongoDB ci si è connessi. La seconda rappresenta il documento che è stato creato nella collezione files. Se infatti ora ci connettiamo con la shell mongo ed effettuiamo una query nel database videos:
> use videos
> db.fs.files.find()
Come risultato vedremo esattamente lo stesso documento. Sempre dalla shell, se invochiamo:
db.fs.chunks.find({}, {data: 0})
otterremo l’elenco dei chunk senza dati in cui è stato suddiviso il file su GridFS. Ad esempio il primo sarà:
{ "_id" : ObjectId("54c25468cc7f1b0526d8f679"), "files_id" : ObjectId("54c25468d
db1e1ed8f269a3c"), "n" : 0 }
Per ottenere i dati di un chunk è sufficiente invocare (in questo caso per il secondo chunk):
db.fs.chunks.find({n:1})
Per eliminare il file la sintassi è simile:
mongofiles -d videos delete mongodb_tutorial.mpg
Se mongofiles può essere utile come strumento di caricamento dati e per l’amministratore del database, per le applicazioni avere un’area di storage facilmente accessibile e con le caratteristiche di disponibilità e performance di MongoDB può essere ancora più utile. Tutti i driver ufficiali supportano GridFS. Qui vedremo un esempio in Java, gli altri driver hanno funzioni analoghe per effettuare le stesse operazioni. Il driver C#, in particolare, ha una API quasi identica, tranne per il nome di qualche classe.
Utilizzo del Driver Java
Vediamo come si può caricare un file utilizzando l’API Java per GridFS. Inseriamo nei commenti la spiegazione dei vari passi:
import java.io.*;
import com.mongodb.*;
import com.mongodb.gridfs.*;
// File da caricare in GridFS
File file = new File("mongodb_tutorial.mpg");
// Connessione a MongoDB (localhost e porta di default)
Mongo client = new Mongo();
// Database in cui memorizzare il file
DB db = mongo.getDB("videos");
// Accesso a GridFS
GridFS gridfs = new GridFS(db);
// Carichiamo il file
GridFSInputFile gfsInputFile = gridfs.createFile(file);
gfsInputFile.setFilename("MongoDB Tutorial");
gfsInputFile.save();
Un fatto interessante qui è che la classe GridFSInputFile
fornisce una serie di metodi per manipolare i metadati inseriti nella collezione files. Per ulteriori dettagli rimandiamo alla guida ufficiale del driver.