In questo articolo vedremo come poter interagire con il File System dei dispositivi mobili attraverso PhoneGap. In questo modo possiamo gestire file e cartelle all'interno delle nostre applicazioni.
Chi non conoscesse questo framework può leggere questa introduzione a PhoneGap e colmare il "gap" :).
Progetto: gestire le ricette con PhoneGap
Per approfondire il tema della gestione dei file ci serviamo di un esempio pratico: creeremo un'applicazione per gestire ricette di cucina.
Il nostro software permetterà all'utente di inserire una ricetta, visualizzarla e caricare un file con tutti i dati su un server. Inoltre, per ampliare la conoscenza degli strumenti forniti da PhoneGap, indicizzeremo questi file con un database locale in modo da interrogare il database per vedere tutte le ricette inserite, invece di estrarle dal contenuto di una cartella.
Ecco gli screenshot dell'applicazione:
Creare file e cartelle con PhoneGap
Come prima cosa creiamo la cartella che conterrà le ricette. Per creare una cartella sullo smartphone dobbiamo richiamare il metodo:
fs.getDirectory("LeMieRicette", {create: true, exclusive: false}, successSave, fail);
dell'oggetto fs
di tipo File System preso con PhoneGap.
Come dicevamo nell'articolo precedente, possiamo associare ad un evento una funzione, in questo caso potremmo gestire l'evento di device_ready
con una funzione che ci permetta di
agganciarci al File System, creando un apposito oggetto e memorizzandolo in un'area che andremo poi a reperire.
Funzioni di scrittura
Iniziamo dalle funzioni di write
dell'applicazione, in modo da poter avere dei dati reali sui quali lavorare in fase di lettura e upload. Vediamo quali sono i passi da compiere:
- Creare il nostro menu di partenza e lavorare sul bottone
Nuova ricetta
- Collegare questo pulsante alla pagina che ci mostrerà un modulo di registrazione dove inserire titolo e testo della ricetta
Vediamo quindi come compierli utilizzando strumenti di PhoneGap.
Aggiungiamo il pulsante con il comando Aggiungi
e associamo un'azione che possiamo definire con un piccolo scriptt nell'header del nostro file HTML. Visto che si tratta di una pagina il cui scopo è solo quello di memorizzare una ricetta, possiamo eseguire tutte le azioni in un click
e creare una serie di procedure in caso di successo e di fallimento. Iniziamo con l'ottenere l'accesso al file system:
function onClick() {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
}
Nel parametro gotFS
possiamo chiedere di ottenere l'oggetto Directory
, crearla se non esiste e, nella callback di successo (directoryFS
) aprire il file e creare quindi un oggetto opportuno da passare alla funzione gotFileEntry
dove il titolo della ricetta sarà il nome del file:
function gotFS(fileSystem) {
fileSystem.root.getDirectory("LeMieRicette", {create: true, exclusive: false}, directoryFS, fail);
}
function directoryFS(fileSystem) {
fileSystem.getFile(document.getElementById('recipe_title').value, {create: true}, gotFileEntry, fail);
}
Una volta creato il file, dobbiamo creare un oggetto di tipo writer
che ci permetterà di scrivere i nostri dati:
function gotFileEntry(fileEntry) {
fileEntry.createWriter(saveRecipe, fail);
}
La funzione principale dell'oggetto writer
è la funzione write, alla quale passiamo i conenuti da salvare.
function saveRecipe(writer) {
writer.write(document.getElementById('recipe_text').value);
}
Possiamo inoltre definire cosa deve succedere una volta invocata write
, passando una funzione all'evento onwrite del writer.
Memorizzare dati su SQLite
Nel nostro caso vogliamo indicizzare il file nel database. I database che vengono utilizzati in Android sono di tipo Sqlite. Per aprire un database è necessario ricorrere alla funzione openDatabase
, alla quale passeremo il nome del database, la versione, il nome visualizzato e la dimensione in byte:
var db = window.openDatabase("myrecipes", "1.0", "Recipes", 200000);
Per eseguire una qualsiasi operazione applicheremo invece il metodo transaction
sull'oggetto appena ottenuto. Il metodo transaction
necessita come parametri la funzione dove si
esegue l'operazione, la callback di errore e quella di successo, quindi nel nostro caso avremo:
db.transaction(populateDB, errorCB, successCB);
Infine costruiamo la nostra funzione di salvataggio più elaborata:
function saveRecipe(writer) {
writer.onwrite = function(evt) {
var db = window.openDatabase("myrecipes", "1.0", "Recipes", 200000);
db.transaction(populateDB, errorCB, successCB);
};
writer.write(document.getElementById('recipe_text').value);
}
function populateDB(tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS Recipes (id INTEGER PRIMARY KEY AUTOINCREMENT, data)');
tx.executeSql('INSERT INTO Recipes (data) VALUES ("' + document.getElementById('recipe_title').value + '")');
}
function errorCB(err) {
alert("Error processing SQL: " + err.code);
}
function successCB() {
alert("success!");
}
Le callback di fallimento e successo sono piuttosto esplicative. Adesso possiamo creare qualche ricetta per poi eseguire il testing nella fase di lettura.
Funzioni di lettura
Vediamo ora come visualizzare una lista di tutte le ricette disponibili e successivamente visualizzarne il testo al click dell'utente.
Per implementare la prima funzione, dobbiamo ricorrere alla lettura del database che indicizza i nostri file e creare un elenco di link. All'evento onLoad
del body andiamo a richiamare la funzione viewRecipes
definita in questo modo:
function viewRecipes() {
var db = window.openDatabase("myrecipes", "1.0", "Recipes", 200000);
db.transaction(queryDB, errorCB);
}
Ancora una volta riapriamo il database e in caso di successo andiamo ad eseguire la nostra query tramite
queryDB
:
function queryDB(tx) {
tx.executeSql('SELECT * FROM Recipes', [], querySuccess, errorCB);
}
In caso di successo la funzione querySuccess
riceverà in ingresso un oggetto results
che rappresenterà la nostra tabella di dati appena presi dal database. Interrogare questa tabella
è molto semplice, una volta che abbiamo ottenuto ll numero di righe della tabella:
var len = results.rows.length;
possiamo ciclare all'interno della struttura prendendo i dati ogni elemento tramite:
result.rows.item(i).data
dove i
è l'indice corrente del nostro ciclo.
Questi dati dovranno essere utilizzati successivamente per creare il nostro elenco, come vediamo dalla funzione completa:
function querySuccess(tx, results) {
var len = results.rows.length;
var menu = "";
for (var i=0; i < len; i++)
menu += '
' + results.rows.item(i).data + '
';
document.getElementById('listRecipe').innerHTML = menu;
}
Nel codice notiamo che il nostro elenco creato, o meglio gli elementi di un elenco, verranno
scritti in un elenco con id listReceipe
. Se eseguite l'applicazione
noterete un look&feel tipico delle applicazioni mobile, specie per iPhone,
ricreare un ambiente simile non è complicato, su questo sito
dove è possibile trovare molti CSS
e file JavaScript
che con poche righe di codice daranno un aspetto molto professionale alla vostra applicazione.
Il link di ogni elemento dell'elenco contiene una pagina alla quale passiamo come parametro il
nome del file della ricetta che visaulizzeremo, ma ricorso ad un trucco per prendere i parametri dopo
il "?":
function getAttributes() {
document.$_GET = [];
var urlHalves = String(document.location).split('?');
if(urlHalves[1]){
var urlVars = urlHalves[1].split('&');
for(var i = 0; i
Non resta che leggere il file, che corrisponderà al parametro ricetta
passato e
che è possibile riprendere chiamando document.$_GET['ricetta']
, procediamo quindi
nello stesso modo fatto per la scrittura:
function showRecipe() {
getAttributes();
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
}
function gotFS(fileSystem) {
fileSystem.root.getDirectory("LeMieRicette", {create: true, exclusive: false}, directoryFS, fail);
}
function directoryFS(fileSystem) {
fileSystem.getFile(document.$_GET['ricetta'], {create: true}, gotFileEntry, fail);
}
function gotFileEntry(fileEntry) {
fileEntry.file(readRecipe, fail);
}
Ovviamente dobbiamo creare un oggetto FileReader
e assegnare all'handler onloadend
la funzione da eseguire in caso di completamento della scrittura. In questo caso andremo a
sovrascrivere l'elemento con id selectedReceipe
che visualizzerà la nostra
ricetta.
function readRecipe(file) {
var reader = new FileReader();
reader.onloadend = function(evt) {
console.log("read success");
console.log(evt.target.result);
document.getElementById('selectedReceipe').innerHTML = evt.target.result;
};
reader.readAsText(file);
}
Upload dei file
Creiamo una routine per l'upload dei file su di un server. Dobbiamo ciclare ogni file
indicizzato sul database allo stesso modo come abbiamo fatto nella lista, ed
infine chiamare la procedura:
function querySuccess(tx, results) {
var len = results.rows.length;
var fileURI = "";
for (var i=0; i < len; i++)
{
fileURI = '/sdcard/LeMieRicette/' + results.rows.item(i).data + '';
var options = new FileUploadOptions();
options.fileName=fileURI.substr(fileURI.lastIndexOf('/')+1);
options.fileKey="file";
options.mimeType="text/plain";
var ft = new FileTransfer();
ft.upload(fileURI, "http://192.168.1.4/uploadRicette/index.php", uploadSuccess, uploadFail, options);
}
}
Ovviamente per testare la funzione dobbiamo avere un server che permetta di fare l'upload del file
e di salvarlo nella propria memoria. Per fare questo basterà uno script PHP:
print_r($_FILES);
move_uploaded_file($_FILES["file"]["tmp_name"], "./uploads/".$_FILES["file"]["name"]);
Potete trovare tutto il codice nell'allegato nella cartella uploadRicette.
Conclusioni
In questo articolo abbiamo visto una delle tante funzioni offerte da PhoneGap, mostrando la sua semplicità nella gestione del File System e l'invio dei file su un server remoto.
Per maggiori informazioni, è possibile visionare la documentazione di PhoneGap riguardanti i
file e lo
storage, ricchi di esempi, direttamente sul sito.