API Storage

Quasi ogni aspetto dello sviluppo di app implica alcuni elementi relativi all'invio o alla ricezione dei dati. Partendo dalle basi, devi utilizzare un framework MVC per progettare e implementare la tua app in modo che i dati siano completamente separati dalla loro visualizzazione su questi dati (vedi Architettura MVC).

Devi anche pensare al modo in cui vengono gestiti i dati quando l'app è offline (consulta la sezione Prima offline). Questo documento illustra brevemente le opzioni di archiviazione per l'invio, la ricezione e il salvataggio dei dati in locale; il resto del documento mostra come utilizzare le API File System e Sync File System di Chrome (vedi anche API fileSystem e API syncFileSystem).

Opzioni di archiviazione

Le app in pacchetto utilizzano molti meccanismi diversi per inviare e ricevere dati. Per i dati esterni (risorse, pagine web), devi conoscere i Criteri di sicurezza del contenuto (CSP). Come per le estensioni di Chrome, puoi utilizzare XMLHttpRequests multiorigine per comunicare con i server remoti. Puoi anche isolare le pagine esterne per proteggere il resto dell'app (consulta Incorporare pagine web esterne).

Quando salvi i dati in locale, puoi utilizzare l'API Chrome Storage per risparmiare piccole quantità di dati stringa e IndexedDB per salvare i dati strutturati. Con IndexedDB, puoi rendere persistenti gli oggetti JavaScript in un archivio di oggetti e utilizzare gli indici dell'archivio per eseguire query sui dati (per ulteriori informazioni, consulta il tutorial sull'elenco di cose da fare di HTML5 Rock). Per tutti gli altri tipi di dati, come i dati binari, utilizza le API Filesystem e Sync Filesystem.

Le API Filesystem e Sync Filesystem di Chrome estendono l'API HTML5 FileSystem. Con l'API Filesystem di Chrome, le app possono creare, leggere, navigare e scrivere in una sezione con sandbox del file system locale dell'utente. Ad esempio, un'app per la condivisione di foto può utilizzare l'API Filesystem per leggere e scrivere le foto selezionate dall'utente.

Con l'API Sync Filesystem di Chrome, le app possono salvare e sincronizzare dati sul Google Drive di un utente, in modo che gli stessi dati siano disponibili su diversi client. Ad esempio, un'app di editor di testo basato su cloud può sincronizzare automaticamente i nuovi file di testo con l'account Google Drive di un utente. Quando l'utente apre l'editor di testo in un nuovo client, Google Drive esegue il push dei nuovi file di testo a quella istanza dell'editor di testo.

Utilizzo dell'API Chrome Filesystem

Aggiunta dell'autorizzazione per il file system

Per utilizzare l'API File System di Chrome, devi aggiungere l'autorizzazione "fileSystem" al file manifest, in modo da ottenere all'utente l'autorizzazione per archiviare dati permanenti.

"permissions": [
  "...",
  "fileSystem"
]

Opzioni utente per la selezione dei file

Gli utenti si aspettano di selezionare i file come fanno sempre. Si aspettano almeno un pulsante "Scegli file" e un selettore file standard. Se la tua app fa un uso intensivo della gestione dei file, devi anche implementare la funzione di trascinamento (leggi sotto e leggi anche la sezione Trascinamento degli asset HTML5 nativi).

Recupero del percorso di un fileEntry

Per ottenere il percorso completo del file selezionato dall'utente, fileEntry, chiama getDisplayPath():

function displayPath(fileEntry) {
  chrome.fileSystem.getDisplayPath(fileEntry, function(path) {
    console.log(path)
  });
}

Implementazione del trascinamento

Se devi implementare la selezione mediante trascinamento, il controller file trascinabile (dnd.js) nell'esempio filesystem-access è un buon punto di partenza. Il controller crea una voce di file da un elemento DataTransferItem tramite trascinamento. In questo esempio, fileEntry è impostato sul primo elemento eliminato.

var dnd = new DnDFileController('body', function(data) {
  var fileEntry = data.items[0].webkitGetAsEntry();
  displayPath(fileEntry);
});

Lettura di un file

Il codice seguente apre il file in sola lettura e lo legge come testo utilizzando un oggetto FileReader. Se il file non esiste, viene visualizzato un errore.

var chosenFileEntry = null;

chooseFileButton.addEventListener('click', function(e) {
  chrome.fileSystem.chooseEntry({type: 'openFile'}, function(readOnlyEntry) {

    readOnlyEntry.file(function(file) {
      var reader = new FileReader();

      reader.onerror = errorHandler;
      reader.onloadend = function(e) {
        console.log(e.target.result);
      };

      reader.readAsText(file);
    });
    });
});

Scrittura di un file

I due casi d'uso comuni per scrivere un file sono "Salva" e "Salva con nome". Il codice seguente crea un elemento writableEntry da chosenFileEntry di sola lettura e scrive il file selezionato al suo interno.

 chrome.fileSystem.getWritableEntry(chosenFileEntry, function(writableFileEntry) {
    writableFileEntry.createWriter(function(writer) {
      writer.onerror = errorHandler;
      writer.onwriteend = callback;

    chosenFileEntry.file(function(file) {
      writer.write(file);
    });
  }, errorHandler);
});

Il codice seguente crea un nuovo file con la funzionalità "Salva con nome" e scrive il nuovo blob nel file utilizzando il metodo writer.write().

chrome.fileSystem.chooseEntry({type: 'saveFile'}, function(writableFileEntry) {
    writableFileEntry.createWriter(function(writer) {
      writer.onerror = errorHandler;
      writer.onwriteend = function(e) {
        console.log('write complete');
      };
      writer.write(new Blob(['1234567890'], {type: 'text/plain'}));
    }, errorHandler);
});

Utilizzo dell'API Chrome Sync Filesystem

Utilizzando l'archiviazione di file sincronizzabile, gli oggetti dati restituiti possono essere gestiti come i file system offline locali nell'API FileSystem, ma con la sincronizzazione aggiunta (e automatica) di tali dati con Google Drive.

Aggiunta dell'autorizzazione di sincronizzazione del file system in corso...

Per utilizzare l'API Sync Filesystem di Chrome, devi aggiungere l'autorizzazione "syncFileSystem" al file manifest, in modo da poter ottenere dall'utente l'autorizzazione per archiviare e sincronizzare i dati permanenti.

"permissions": [
  "...",
  "syncFileSystem"
]

Avvio dell'archiviazione di file sincronizzabili

Per avviare l'archiviazione di file sincronizzabile nella tua app, chiama syncFileSystem.requestFileSystem. Questo metodo restituisce un file system sincronizzabile supportato da Google Drive, ad esempio:

chrome.syncFileSystem.requestFileSystem(function (fs) {
   // FileSystem API should just work on the returned 'fs'.
   fs.root.getFile('test.txt', {create:true}, getEntryCallback, errorCallback);
});

Informazioni sullo stato di sincronizzazione dei file

Utilizza syncFileSystem.getFileStatus per ottenere lo stato di sincronizzazione di un file corrente:

chrome.syncFileSystem.getFileStatus(entry, function(status) {...});

I valori dello stato di sincronizzazione dei file possono essere uno dei seguenti: 'synced', 'pending' o 'conflicting'. "Sincronizzato" significa che il file è completamente sincronizzato; non ci sono modifiche locali in attesa che non siano state sincronizzate con Google Drive. Tuttavia, sul lato Google Drive possono esserci modifiche in attesa che non sono ancora state recuperate.

"In attesa" significa che il file contiene modifiche in attesa non ancora sincronizzate con Google Drive. Se l'app è in esecuzione online, le modifiche locali vengono (quasi) immediatamente sincronizzate con Google Drive e l'evento syncFileSystem.onFileStatusChanged viene attivato con lo stato 'synced' (vedi di seguito per maggiori dettagli).

syncFileSystem.onFileStatusChanged viene attivato quando lo stato di un file passa a 'conflicting'. "In conflitto" significa che sono presenti modifiche in conflitto sia nello spazio di archiviazione locale che in Google Drive. Un file può essere in questo stato solo se il criterio di risoluzione dei conflitti è impostato su 'manual'. La norma predefinita è 'last_write_win' e i conflitti vengono risolti automaticamente mediante una semplice norma basata sull'ultima scrittura vincente. Il criterio di risoluzione dei conflitti del sistema può essere modificato da syncFileSystem.setConflictResolutionPolicy.

Se il criterio di risoluzione dei conflitti è impostato su 'manual' e un file ha lo stato 'conflicting', l'app può comunque leggere e scrivere il file come file offline locale, ma le modifiche non vengono sincronizzate e il file verrà mantenuto scollegato dalle modifiche remote apportate su altri client fino alla risoluzione del conflitto. Il modo più semplice per risolvere un conflitto è eliminare o rinominare la versione locale del file. Questa operazione forza la sincronizzazione della versione remota, lo stato in conflitto viene risolto e l'evento onFileStatusChanged viene attivato con lo stato 'synced'.

In ascolto di modifiche allo stato sincronizzato

L'evento syncFileSystem.onFileStatusChanged viene attivato quando lo stato di sincronizzazione di un file cambia. Ad esempio, supponiamo che un file abbia modifiche in attesa e sia nello stato "In attesa". L'app potrebbe essere in stato offline, pertanto la modifica sta per essere sincronizzata. Quando il servizio di sincronizzazione rileva la modifica locale in attesa e carica la modifica su Google Drive, attiva l'evento onFileStatusChanged con i seguenti valori: { fileEntry:a fileEntry for the file, status: 'synced', action: 'updated', direction: 'local_to_remote' }.

Allo stesso modo, indipendentemente dalle attività locali, il servizio di sincronizzazione potrebbe rilevare modifiche remote apportate da un altro client e scaricarle da Google Drive allo spazio di archiviazione locale. Se la modifica remota riguardava l'aggiunta di un nuovo file, viene attivato un evento con i seguenti valori: { fileEntry: a fileEntry for the file, status: 'synced', action: 'added', direction: 'remote_to_local' }.

Se sia il lato locale sia il lato remoto presentano modifiche in conflitto per lo stesso file e se il criterio di risoluzione del conflitto è impostato su 'manual', lo stato del file viene modificato in stato conflicting, viene scollegato dal servizio di sincronizzazione e non verrà sincronizzato finché il conflitto non sarà risolto. In questo caso viene attivato un evento con i seguenti valori: { fileEntry: a fileEntry for the file, status: 'conflicting', action: null, direction: null }.

Puoi aggiungere un listener per questo evento che risponda a qualsiasi cambiamento di stato. Ad esempio, l'app Chrome Music Player ascolta tutta la nuova musica sincronizzata da Google Drive, ma non ancora importata nello spazio di archiviazione locale dell'utente su un determinato client. Tutta la musica trovata viene sincronizzata con il client:

chrome.syncFileSystem.onFileStatusChanged.addListener(function(fileInfo) {
  if (fileInfo.status === 'synced') {
    if (fileInfo.direction === 'remote_to_local') {
      if (fileInfo.action === 'added') {
        db.add(fileInfo.fileEntry);
      } else if (fileInfo.action === 'deleted') {
        db.remove(fileInfo.fileEntry);
      }
    }
  }
});

Controllo dell'utilizzo dell'API

Per verificare la quantità di dati utilizzati dall'API, esegui una query nella directory sandbox locale dell'app o sui byte di utilizzo restituiti da syncFileSystem.getUsageAndQuota:

chrome.syncFileSystem.getUsageAndQuota(fileSystem, function (storageInfo) {
   updateUsageInfo(storageInfo.usageBytes);
   updateQuotaInfo(storageInfo.quotaBytes);
});

Puoi anche controllare lo spazio di archiviazione del servizio di backend di sincronizzazione dell'utente (in Google Drive). I file sincronizzati vengono salvati in una cartella nascosta di Google Drive, Chrome Syncable FileSystem. La cartella non verrà visualizzata nell'elenco "Il mio Drive", ma sarà possibile accedervi cercandone il nome nella casella di ricerca. Tieni presente che non è garantito che il layout della cartella remota rimanga compatibile con le versioni precedenti tra le versioni.