Giocare con l'API del file system HTML5

HTML5 ci offre un'intera serie di nuove possibilità, come disegnare su tela, implementare contenuti multimediali con le API audio e video e così via. Uno di questi strumenti, che è ancora relativamente nuovo, è l'API del file system. Ci dà accesso a una sezione in modalità sandbox del file system locale dell'utente, riempiendo ulteriormente il divario tra desktop e applicazioni Web! Nel tutorial di oggi, illustreremo le basi di questa nuova ed entusiasmante API, esplorando le più comuni attività del filesystem. Iniziamo!


introduzione

Non è più necessario scaricare e installare un determinato software per poterlo utilizzare. Semplicemente un browser web e una connessione internet ci danno la possibilità di utilizzare qualsiasi applicazione web, sempre, ovunque e su qualsiasi piattaforma.

In breve, le app Web sono fantastiche; ma, rispetto alle app desktop, hanno ancora una debolezza significativa: non hanno un modo di interagire e organizzare i dati in una gerarchia strutturata di cartelle - un vero file system. Fortunatamente, con la nuova API di Filesystem, questo può essere modificato. Questa API fornisce alle applicazioni Web l'accesso controllato a una "sandbox" locale di file system privata in cui possono scrivere e leggere file, creare ed elencare directory e così via. Sebbene al momento in cui scrivo solo il browser Chrome di Google supporta l'implementazione "completa" dell'API del Filesystem, merita comunque di essere studiato come una forma potente e conveniente di archiviazione locale.

L'API di Filesystem è disponibile in due versioni differenti. L'API asincrona, che è utile per le normali applicazioni, e l'API sincrona, riservata per l'uso con i lavoratori del web. Ai fini di questo tutorial, esploreremo esclusivamente la versione asincrona dell'API.


Passaggio 1: Introduzione

Il primo passo è ottenere l'accesso al filesystem HTML5 richiedendo a localfile Oggetto di sistema, usando il window.requestFileSystem () metodo globale:

window.requestFileSystem (tipo, dimensione, successCallback, opt_errorCallback)

Non c'è modo per un'applicazione web di "scoppiare" oltre la directory radice locale.

Come i primi due parametri, si specifica la durata e la dimensione del filesystem che si desidera. UN PERSISTENTE il filesystem è adatto per le app Web che desiderano memorizzare i dati dell'utente in modo permanente. Il browser non lo eliminerà, tranne che per la richiesta esplicita dell'utente. UN TEMPORANEO il filesystem è appropriato per le app Web che desiderano memorizzare i dati nella cache, ma possono ancora funzionare se il browser Web elimina il filesystem. La dimensione del filesystem è specificata in byte e dovrebbe essere un limite superiore ragionevole alla quantità di dati che è necessario memorizzare.

Il terzo parametro è una funzione di callback attivata quando l'agente utente fornisce correttamente un filesystem. La sua argomentazione è a FileSystem oggetto. E, infine, possiamo aggiungere una funzione di callback opzionale, che viene chiamata quando si verifica un errore, o la richiesta di un filesystem è negata. La sua argomentazione è a FileError oggetto. Sebbene questo parametro sia facoltativo, è sempre una buona idea catturare gli errori per gli utenti, poiché ci sono un certo numero di punti in cui le cose possono andare storte.

Il filesystem ottenuto con queste funzioni dipende dall'origine del documento che lo contiene. Tutti i documenti o le app Web dalla stessa origine (host, porta e protocollo) condividono un filesystem. Due documenti o applicazioni di origini diverse hanno filesystem completamente distinti e disgiunti. Un filesystem è limitato a una singola applicazione e non può accedere ai dati memorizzati di un'altra applicazione. È anche isolato dal resto dei file sul disco rigido dell'utente, il che è una buona cosa: non c'è modo per un'applicazione web di "uscire" oltre la directory root locale o comunque accedere a file arbitrari.

Rivediamo un esempio:

window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; window.requestFileSystem (window.TEMPORARY, 5 * 1024 * 1024, initFS, errorHandler); function initFS (fs) alert ("Benvenuto in Filesystem! It's showtime :)"); // Solo per verificare se tutto è OK :) // posiziona le funzioni che imparerai qui sotto function errorHandler () console.log ('Un errore si è verificato'); 

Questo crea un filesystem temporaneo con 5 MB di spazio di archiviazione. Fornisce quindi una funzione di callback di successo, che useremo per gestire il nostro filesystem. E, naturalmente, viene anche aggiunto un gestore di errori - nel caso in cui qualcosa vada storto. Qui, il errorHandler () la funzione è troppo generica. Quindi, se lo desideri, puoi creare una versione leggermente ottimizzata, che offre al lettore un messaggio di errore più descrittivo:

function errorHandler (err) var msg = 'Si è verificato un errore:'; switch (err.code) case FileError.NOT_FOUND_ERR: msg + = 'File o directory non trovata'; rompere; caso FileError.NOT_READABLE_ERR: msg + = 'File o directory non leggibile'; rompere; caso FileError.PATH_EXISTS_ERR: msg + = 'File o directory esiste già'; rompere; caso FileError.TYPE_MISMATCH_ERR: msg + = 'Tipo file non valido'; rompere; default: msg + = 'Errore sconosciuto'; rompere; ; console.log (msg); ;

L'oggetto del filesystem che si ottiene ha a nome (un nome univoco per il filesystem, assegnato dal browser) e radice proprietà che fa riferimento alla directory root del filesystem. Questo è un DirectoryEntry oggetto, e potrebbe avere directory annidate che sono esse stesse rappresentate da DirectoryEntry oggetti. Ogni directory nel file system può contenere file, rappresentati da FileEntry oggetti. Il DirectoryEntry oggetto definisce i metodi per ottenere DirectoryEntry e FileEntry oggetti in base al nome del percorso (facoltativamente creeranno nuove directory o file se specifichi un nome che non esiste). DirectoryEntry definisce anche a createReader () metodo di fabbrica che restituisce a DirectoryReader oggetto per elencare il contenuto di una directory. Il FileEntry class definisce un metodo per ottenere il File oggetto (un Blob) che rappresenta il contenuto di un file. È quindi possibile utilizzare a FileReader oggetto per leggere il file. FileEntry definisce un altro metodo per restituire a FileWriter oggetto che è possibile utilizzare per scrivere contenuti in un file.

Phhew ... sembra complicato? Non ti preoccupare Tutto diventerà più chiaro man mano che progrediamo attraverso gli esempi qui sotto.


Passaggio 2: utilizzo delle directory

Ovviamente, la prima cosa che devi creare in un filesystem è qualche bucket o directory. Sebbene la directory root esista già, non si desidera posizionare tutti i file lì. Le directory sono create da DirectoryEntry oggetto. Nell'esempio seguente, creiamo una directory, chiamata Documenti, all'interno della directory principale:

fs.root.getDirectory ('Documenti', create: true, function (dirEntry) alert ('Hai appena creato la directory' + dirEntry.name + '.');, errorHandler);

Il GetDirectory () metodo è usato sia per leggere e creare directory. Come primo parametro, puoi passare un nome o un percorso come directory da cercare o creare. Impostiamo il secondo argomento su vero, perché stiamo tentando di creare una directory, non di leggerne una esistente. E alla fine, aggiungiamo un callback di errore.

Fin qui tutto bene. Abbiamo una directory; aggiungiamo ora una sottodirectory. La funzione è esattamente la stessa con una differenza: cambiamo il primo argomento da "Documenti" a "Documenti / Musica". Abbastanza facile; ma cosa succede se si desidera creare una sottocartella, Cielo, con due cartelle principali, immagini e Natura, dentro il Documenti cartella? Se si digita 'Documenti / Immagini / Natura / Sky'per l'argomento path, riceverai un errore, perché non puoi creare una directory, quando il suo genitore immediato non esiste. Una soluzione per questo è creare ciascuna cartella una per una: immagini dentro Documenti, Natura dentro immagini, e poi Cielo dentro Natura. Ma questo è un processo molto lento e scomodo. C'è una soluzione migliore: creare una funzione che creerà automaticamente tutte le cartelle necessarie.

function createDir (rootDir, folders) rootDir.getDirectory (cartelle [0], create: true, function (dirEntry) if (folders.length) createDir (dirEntry, folders.slice (1));, errorHandler); ; createDir (fs.root, 'Documents / Images / Nature / Sky /'. split ('/'));

Con questo piccolo trucco, tutto ciò che dobbiamo fare è fornire un percorso completo che rappresenti le cartelle che vogliamo creare. Ora il Cielo la directory è stata creata correttamente ed è possibile creare altri file o directory al suo interno.

Ora è il momento di controllare cosa abbiamo nel nostro filesystem. Creeremo un DirectoryReader oggetto e utilizzare il readEntries () metodo per leggere il contenuto della directory.

fs.root.getDirectory ('Documenti', , funzione (dirEntry) 
var dirReader = dirEntry.createReader (); dirReader.readEntries (function (entries)
per (var i = 0; i < entries.length; i++) var entry = entries[i]; if (entry.isDirectory) console.log('Directory: ' + entry.fullPath); else if (entry.isFile) console.log('File: ' + entry.fullPath); , errorHandler); , errorHandler);

Nel codice sopra, il isDirectory e isFile le proprietà vengono utilizzate al fine di ottenere un output diverso per directory e file, rispettivamente. Inoltre, usiamo il percorso completo proprietà per ottenere il percorso completo della voce, anziché solo il suo nome.

Ci sono due modi per rimuovere a DirectoryEntry dal filesystem: rimuovere() e removeRecursively (). Il primo rimuove una determinata directory solo se è vuota. Altrimenti, riceverai un errore.

fs.root.getDirectory ('Documenti / Musica', , funzione (dirEntry) dirEntry.remove (function () console.log ('Directory rimossa con successo.');, errorHandler);, errorHandler);

Se la Musica la cartella contiene file, quindi è necessario utilizzare il secondo metodo, che elimina in modo ricorsivo la directory e tutti i relativi contenuti.

fs.root.getDirectory ('Documents / Music', , function (dirEntry) dirEntry.removeRecursively (function () console.log ('Directory rimosso con successo.');, errorHandler);, errorHandler);

Passaggio 3: utilizzo dei file

Ora che sappiamo come creare le directory, è il momento di popolarle con i file!

L'esempio seguente crea un vuoto test.txt nella directory principale:

fs.root.getFile ('test.txt', create: true, exclusive: true, function (fileEntry) alert ('Un file' + fileEntry.name + 'è stato creato con successo.');, errorHandler) ;

Il primo argomento a prendi il file() può essere un percorso assoluto o relativo, ma deve essere valido. Ad esempio, è un errore tentare di creare un file, quando il suo genitore immediato non esiste. Il secondo argomento è un oggetto letterale, che descrive il comportamento della funzione se il file non esiste. In questo esempio, creare: vero crea il file se non esiste e genera un errore se lo fa (esclusivo: vero). Altrimenti, se creare: falso, il file viene semplicemente scaricato e restituito.

Avere un file vuoto non è molto utile, però; quindi aggiungiamo alcuni contenuti all'interno. Possiamo usare il FileWriter oggetto per questo.

fs.root.getFile ('test.txt', create: false, function (fileEntry) fileEntry.createWriter (function (fileWriter) window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder; var bb = new BlobBuilder (); bb.append ('API filesystem è fantastica!'); fileWriter.write (bb.getBlob ('text / plain'));, errorHandler);, errorHandler);

Sopra, recuperiamo il test.txt file e creare a FileWriter oggetto per questo. Quindi aggiungiamo il contenuto creando un nuovo BlobBuilder oggetto e usando il Scrivi() metodo di FileWriter.

chiamata prendi il file() recupera solo a FileEntry. Non restituisce il contenuto del file. Quindi, se vogliamo leggere il contenuto del file, dobbiamo usare il File oggetto e il FileReader oggetto.

fs.root.getFile ('test.txt', , function (fileEntry) fileEntry.file (function (file) var reader = new FileReader (); reader.onloadend = function (e) alert (questo. risultato);; reader.readAsText (file);, errorHandler);, errorHandler);

Abbiamo scritto alcuni contenuti nel nostro file, ma cosa accadrebbe se desiderassimo aggiungerne altri in un secondo momento? Per aggiungere dati a un file esistente, il file FileWriter è usato ancora una volta. Possiamo riposizionare lo scrittore alla fine del file, usando il cercare() metodo. cercare accetta un offset di byte come argomento e imposta la posizione dello scrittore di file su tale offset.

fs.root.getFile ('test.txt', create: false, function (fileEntry) fileEntry.createWriter (function (fileWriter) fileWriter.seek (fileWriter.length); window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder; var bb = new BlobBuilder (); bb.append ('Sì, lo è!'); fileWriter.write (bb.getBlob ('text / plain'));, errorHandler);, errorHandler) ;

Per rimuovere un file dal filesystem, basta chiamare entry.remove (). Il primo argomento di questo metodo è una funzione di callback con parametri zero, che viene chiamata quando il file viene eliminato correttamente. Il secondo è un callback di errore opzionale se si verificano errori.

fs.root.getFile ('test.txt', create: false, function (fileEntry) fileEntry.remove (function () console.log ('File rimosso con successo.');, errorHandler);, errorHandler);

Passaggio 4: Manipolazione di file e directory

FileEntry e DirectoryEntry condividere gli stessi metodi API per copiare, spostare e rinominare le voci. Esistono due metodi che è possibile utilizzare per queste operazioni: copia a() e moveTo (). Entrambi accettano gli stessi identici parametri:

copyTo (parentDirEntry, opt_newName, opt_successCallback, opt_errorCallback); moveTo (parentDirEntry, opt_newName, opt_successCallback, opt_errorCallback);

Il primo parametro è la cartella padre per spostare / copiare la voce in. Il secondo è un nuovo nome opzionale per dare la voce spostata / copiata, che è effettivamente necessaria quando si copia una voce nella stessa cartella; altrimenti otterrai un errore. Il terzo e il quarto parametro sono stati spiegati in precedenza.

Rivediamo alcuni semplici esempi. Nel seguente, copiamo il file test.txt dal radice al Documenti elenco.

function copy (currDir, srcEntry, destDir) currDir.getFile (srcEntry, , function (fileEntry) currDir.getDirectory (destDir, , function (dirEntry) fileEntry.copyTo (dirEntry);, errorHandler); , errorHandler);  copy (fs.root, 'test.txt', 'Documents /');

Questo prossimo esempio si muove test.txt a Documenti, invece di copiarlo:

function move (currDir, srcEntry, dirName) currDir.getFile (srcEntry, , function (fileEntry) currDir.getDirectory (dirName, , function (dirEntry) fileEntry.moveTo (dirEntry);, errorHandler); , errorHandler);  move (fs.root, 'test.txt', 'Documents /');

Il seguente esempio rinomina test.txt a text.txt:

function rename (currDir, srcEntry, newName) currDir.getFile (srcEntry, , function (fileEntry) fileEntry.moveTo (currDir, newName);, errorHandler);  rinomina (fs.root, 'test.txt', 'text.txt');

Per saperne di più

In questo tutorial introduttivo, abbiamo solo graffiato la superficie delle diverse interfacce del filesystem. Se vuoi saperne di più e approfondire l'API di Filesystem, dovresti fare riferimento alle specifiche tecniche del W3C:

  • File API: directory e sistema
  • File API: Writer
  • File API

Ora che hai una conoscenza di base di cosa sia l'API di Filesystem e di come può essere utilizzata, dovrebbe essere notevolmente più semplice comprendere la documentazione dell'API, che può essere un po 'confusa a prima vista.


Conclusione

L'API di Filesystem è una tecnologia potente e facile da usare, che offre agli sviluppatori web nuove opportunità quando costruisce applicazioni web. Certo, è ancora abbastanza nuovo e non ampiamente supportato da tutti i principali browser, ma questo cambierà sicuramente in futuro. Si potrebbe anche ottenere un vantaggio!