Spazio di archiviazione ad alte prestazioni per la tua app: l'API Storage Foundation

Thomas Steiner
Thomas Steiner

La piattaforma web offre sempre più agli sviluppatori gli strumenti di cui hanno bisogno per creare applicazioni ad alte prestazioni ottimizzate per il web. In particolare, WebAssembly (Wasm) ha aperto la strada ad applicazioni web rapide e potenti, mentre tecnologie come Emscripten ora consentono agli sviluppatori di riutilizzare codice collaudato sul web. Per sfruttare al meglio questo potenziale, gli sviluppatori devono avere la stessa potenza e flessibilità in termini di archiviazione.

È qui che entra in gioco l'API Storage Foundation. L'API Storage Foundation è una nuova API di archiviazione rapida e senza opinioni che sblocca nuovi casi d'uso molto richiesti per il web, come l'implementazione di database performanti e la gestione elegante di file temporanei di grandi dimensioni. Con questa nuova interface, gli sviluppatori possono "portare il proprio spazio di archiviazione" sul web, riducendo il divario di funzionalità tra il codice web e quello specifico della piattaforma.

L'API Storage Foundation è progettata per assomigliare a un file system molto basilare, quindi offre agli sviluppatori flessibilità offrendo primitive generiche, semplici ed efficienti su cui possono creare componenti di livello superiore. Le applicazioni possono sfruttare lo strumento migliore per le loro esigenze, trovando il giusto equilibrio tra usabilità, prestazioni e affidabilità.

Perché il web ha bisogno di un'altra API di archiviazione?

La piattaforma web offre una serie di opzioni di archiviazione per gli sviluppatori, ognuna delle quali è progettata in base a casi d'uso specifici.

  • È evidente che alcune di queste opzioni non si sovrappongono a questa proposta perché consentono di archiviare solo quantità molto ridotte di dati, come i cookie o l'API Web Storage composta dai meccanismi sessionStorage e localStorage.
  • Altre opzioni sono già state ritirate per vari motivi, ad esempio l'API File and Directory Entries o WebSQL.
  • L'API File System Access ha una superficie API simile, ma il suo utilizzo consiste nell'interfacciarsi con il file system del client e fornire accesso a dati che potrebbero non essere di proprietà dell'origine o persino del browser. Questo diverso approccio comporta considerazioni di sicurezza più stringenti e costi di prestazioni più elevati.
  • L'API IndexedDB può essere utilizzata come backend per alcuni casi d'uso dell'API Storage Foundation. Ad esempio, Emscripten include IDBFS, un sistema di file persistente basato su IndexedDB. Tuttavia, poiché IndexedDB è fondamentalmente un archivio di chiavi e valori, presenta limitazioni significative del rendimento. Inoltre, l'accesso diretto alle sottosezioni di un file è ancora più difficile e più lento in IndexedDB.
  • Infine, l'interfaccia CacheStorage è supportata ampiamente ed è ottimizzata per l'archiviazione di dati di grandi dimensioni, come le risorse delle applicazioni web, ma i valori sono immutabili.

L'API Storage Foundation è un tentativo di colmare tutte le lacune delle opzioni di archiviazione precedenti consentendo l'archiviazione ad alte prestazioni di file di grandi dimensioni modificabili definiti all'interno dell'origine dell'applicazione.

Casi d'uso suggeriti per l'API Storage Foundation

Ecco alcuni esempi di siti che potrebbero utilizzare questa API:

  • App di produttività o creatività che utilizzano grandi quantità di dati video, audio o immagine. Queste app possono scaricare i segmenti su disco anziché conservarli in memoria.
  • App che si basano su un file system permanente accessibile da Wasm e che richiedono prestazioni superiori a quelle garantite da IDBFS.

Che cos'è l'API Storage Foundation?

L'API è composta da due parti principali:

  • Chiamate al file system, che forniscono funzionalità di base per interagire con file e percorsi file.
  • Handle dei file, che forniscono l'accesso in lettura e scrittura a un file esistente.

Chiamate al file system

L'API Storage Foundation introduce un nuovo oggetto, storageFoundation, che si trova nell'oggetto window e che include una serie di funzioni:

  • storageFoundation.open(name): apre il file con il nome specificato, se esistente, altrimenti crea un nuovo file. Restituisce una promessa che si risolve con il file aperto.
  • storageFoundation.delete(name): rimuove il file con il nome specificato. Restituisce una promessa che si risolve quando il file viene eliminato.
  • storageFoundation.rename(oldName, newName): rinomina il file dal vecchio nome al nuovo nome in modo atomico. Restituisce una promessa che si risolve quando il file viene rinominato.
  • storageFoundation.getAll(): restituisce una promessa che si risolve con un array di tutti i nomi di file esistenti.
  • storageFoundation.requestCapacity(requestedCapacity): richiede nuova capacità (in byte) per l'utilizzo da parte del contesto di esecuzione corrente. Restituisce una promessa risolta con la quantità rimanente di capacità disponibile.
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): rilascia il numero specificato di byte dal contesto di esecuzione corrente e restituisce una promessa che si risolve con la capacità rimanente.
  • storageFoundation.getRemainingCapacity(): restituisce una promessa che si risolve con la capacità disponibile per il contesto di esecuzione corrente.

Handle dei file

Il lavoro con i file avviene tramite le seguenti funzioni:

  • NativeIOFile.close(): chiude un file e restituisce una promessa che si risolve al completamento dell'operazione.
  • NativeIOFile.flush(): sincronizza (ovvero svuota la cache) lo stato in memoria di un file con il dispositivo di archiviazione e restituisce una promessa che si risolve al termine dell'operazione.
  • NativeIOFile.getLength(): restituisce una promessa che si risolve con la lunghezza del file in byte.
  • NativeIOFile.setLength(length): imposta la lunghezza del file in byte e restituisce una promessa che si risolve al completamento dell'operazione. Se la nuova lunghezza è inferiore a quella attuale, i byte vengono rimossi a partire dalla fine del file. In caso contrario, il file viene esteso con byte a valore zero.
  • NativeIOFile.read(buffer, offset): legge i contenuti del file all'offset specificato tramite un buffer che è il risultato del trasferimento del buffer specificato, che viene poi lasciato scollegato. Restituisce un NativeIOReadResult con il buffer trasferito e il numero di byte che sono stati letti correttamente.

    Un NativeIOReadResult è un oggetto costituito da due voci:

    • buffer: un ArrayBufferView, che è il risultato del trasferimento del buffer passato a read(). È dello stesso tipo e della stessa lunghezza del buffer di origine.
    • readBytes: il numero di byte letti correttamente in buffer. Può essere inferiore alle dimensioni del buffer se si verifica un errore o se l'intervallo di lettura si estende oltre la fine del file. Viene impostato su zero se l'intervallo di lettura supera la fine del file.
  • NativeIOFile.write(buffer, offset): scrive i contenuti del buffer specificato nel file all'offset specificato. Il buffer viene trasferito prima che i dati vengano scritti e viene quindi lasciato scollegato. Restituisce un valore NativeIOWriteResult con il buffer trasferito e il numero di byte scritti correttamente. Il file verrà esteso se l'intervallo di scrittura supera la sua lunghezza.

    Un NativeIOWriteResult è un oggetto costituito da due voci:

    • buffer: un elemento ArrayBufferView che è il risultato del trasferimento del buffer passato a write(). È dello stesso tipo e della stessa lunghezza del buffer di origine.
    • writtenBytes: il numero di byte scritti correttamente in buffer. Se si verifica un errore, questo valore potrebbe essere inferiore alla dimensione del buffer.

Esempi completi

Per rendere più chiari i concetti introdotti in precedenza, ecco due esempi completi che illustrano le diverse fasi del ciclo di vita dei file di Storage Foundation.

Apertura, scrittura, lettura, chiusura

// Open a file (creating it if needed).
const file = await storageFoundation.open('test_file');
try {
  // Request 100 bytes of capacity for this context.
  await storageFoundation.requestCapacity(100);

  const writeBuffer = new Uint8Array([64, 65, 66]);
  // Write the buffer at offset 0. After this operation, `result.buffer`
  // contains the transferred buffer and `result.writtenBytes` is 3,
  // the number of bytes written. `writeBuffer` is left detached.
  let result = await file.write(writeBuffer, 0);

  const readBuffer = new Uint8Array(3);
  // Read at offset 1. `result.buffer` contains the transferred buffer,
  // `result.readBytes` is 2, the number of bytes read. `readBuffer` is left
  // detached.
  result = await file.read(readBuffer, 1);
  // `Uint8Array(3) [65, 66, 0]`
  console.log(result.buffer);
} finally {
  file.close();
}

Apertura, elenco, eliminazione

// Open three different files (creating them if needed).
await storageFoundation.open('sunrise');
await storageFoundation.open('noon');
await storageFoundation.open('sunset');
// List all existing files.
// `["sunset", "sunrise", "noon"]`
await storageFoundation.getAll();
// Delete one of the three files.
await storageFoundation.delete('noon');
// List all remaining existing files.
// `["sunrise", "noon"]`
await storageFoundation.getAll();

Demo

Puoi provare la demo dell'API Storage Foundation nell'embed di seguito. Crea, rinomina, scrivi in e leggi da file e visualizza l'aggiornamento della capacità disponibile che hai richiesto man mano che apporti modifiche. Puoi trovare il codice sorgente della demo su Glitch.

Sicurezza e autorizzazioni

Il team di Chromium ha progettato e implementato l'API Storage Foundation utilizzando i principi fondamentali definiti in Controllo dell'accesso a potenti funzionalità della piattaforma web, tra cui il controllo dell'utente, la trasparenza e l'ergonomia.

Seguendo lo stesso schema di altre API di archiviazione moderne sul web, l'accesso all'API Storage Foundation è vincolato all'origine, il che significa che un'origine può accedere solo ai dati creati autonomamente. Inoltre, è limitata ai contesti sicuri.

Controllo utente

La quota di spazio di archiviazione verrà utilizzata per distribuire l'accesso allo spazio su disco e per impedire utilizzi illeciti. La memoria che vuoi occupare deve essere richiesta prima. Come per le altre API di archiviazione, gli utenti possono liberare lo spazio occupato dall'API Storage Foundation tramite il browser.

Link utili

Ringraziamenti

L'API Storage Foundation è stata specificata e implementata da Emanuel Krivoy e Richard Stotz. Questo articolo è stato esaminato da Pete LePage e Joe Medley.

Immagine hero di Markus Spiske su Unsplash.