La piattaforma web offre sempre più spesso agli sviluppatori gli strumenti necessari per creare applicazioni ad alte prestazioni ottimizzate per il web. In particolare, WebAssembly (Wasm) ha aperto le porte ad applicazioni web veloci e potenti, mentre tecnologie come Emscripten ora consentono agli sviluppatori di riutilizzare codice collaudato sul web. Per sfruttare appieno questo potenziale, gli sviluppatori devono avere la stessa potenza e flessibilità in termini di spazio 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 casi d'uso nuovi e molto richiesti per il web, come l'implementazione di database performanti e la gestione corretta di file temporanei di grandi dimensioni. Con questa nuova interfaccia, gli sviluppatori possono "portare il proprio spazio di archiviazione" sul web, riducendo il divario di funzionalità tra il web e il codice specifico della piattaforma.
L'API Storage Foundation è progettata per assomigliare a un file system molto semplice, in modo da offrire agli sviluppatori flessibilità fornendo primitive generiche, semplici e performanti 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 Storage?
La piattaforma web offre una serie di opzioni di archiviazione per gli sviluppatori, ognuna delle quali è stata creata pensando a casi d'uso specifici.
- Alcune di queste opzioni non si sovrappongono chiaramente a questa proposta, in quanto consentono di archiviare solo quantità molto piccole di dati, come i cookie o l'API Web Storage, costituita dai meccanismi
sessionStorage
elocalStorage
. - Altre opzioni sono già deprecate 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 è quello di interagire con il file system del client e fornire l'accesso a dati che potrebbero non essere di proprietà dell'origine o persino del browser. Questo diverso approccio comporta considerazioni di sicurezza più rigorose e costi di rendimento 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 file system persistente basato su IndexedDB. Tuttavia, poiché IndexedDB è fondamentalmente un archivio chiave-valore, presenta limitazioni significative delle prestazioni. Inoltre, l'accesso diretto alle sottosezioni di un file è ancora più difficile e lento in IndexedDB.
- Infine, l'interfaccia CacheStorage è ampiamente supportata e 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 precedenti opzioni di archiviazione consentendo l'archiviazione efficiente 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 operano su grandi quantità di dati video, audio o immagine. Queste app possono scaricare i segmenti sul disco anziché conservarli in memoria.
- App che si basano su un file system permanente accessibile da Wasm e che richiedono prestazioni superiori a quelle che IDBFS può garantire.
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 dei file.
- Handle di 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 esiste, altrimenti ne crea uno nuovo. Restituisce una promessa che viene risolta con il file aperto.
storageFoundation.delete(name)
: rimuove il file con il nome specificato. Restituisce una promessa che viene risolta 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 viene risolta quando il file viene rinominato.storageFoundation.getAll()
: restituisce una promessa che viene risolta con un array di tutti i nomi dei file esistenti.storageFoundation.requestCapacity(requestedCapacity)
: Richiede una nuova capacità (in byte) per l'utilizzo da parte del contesto di esecuzione corrente. Restituisce una promessa risolta con l'importo rimanente della capacità disponibile.
storageFoundation.releaseCapacity(toBeReleasedCapacity)
: rilascia il numero specificato di byte dal contesto di esecuzione corrente e restituisce una promessa che viene risolta con la capacità rimanente.storageFoundation.getRemainingCapacity()
: restituisce una promessa che viene risolta con la capacità disponibile per il contesto di esecuzione corrente.
Handle di file
L'utilizzo dei file avviene tramite le seguenti funzioni:
NativeIOFile.close()
: chiude un file e restituisce una promessa che viene risolta al termine dell'operazione.NativeIOFile.flush()
: sincronizza (ovvero svuota) lo stato in memoria di un file con il dispositivo di archiviazione e restituisce una promessa che viene risolta al termine dell'operazione.
NativeIOFile.getLength()
: restituisce una promessa che viene risolta con la lunghezza del file in byte.NativeIOFile.setLength(length)
: imposta la lunghezza del file in byte e restituisce una promessa che viene risolta al termine 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 con 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 unNativeIOReadResult
con il buffer trasferito e il numero di byte letti correttamente.Un
NativeIOReadResult
è un oggetto costituito da due voci:buffer
: unArrayBufferView
, che è il risultato del trasferimento del buffer passato aread()
. È dello stesso tipo e lunghezza del buffer di origine.readBytes
: il numero di byte letti correttamente inbuffer
. Questo valore potrebbe essere inferiore alle dimensioni del buffer se si verifica un errore o se l'intervallo di lettura si estende oltre la fine del file. È impostato su zero se l'intervallo di lettura si trova oltre 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 vengano scritti i dati e viene quindi lasciato separato. Restituisce unNativeIOWriteResult
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
: unArrayBufferView
che è il risultato del trasferimento del buffer passato awrite()
. È dello stesso tipo e lunghezza del buffer di origine.writtenBytes
: il numero di byte scritti correttamente inbuffer
. Questo valore potrebbe essere inferiore alla dimensione del buffer se si verifica un errore.
Esempi completi
Per rendere più chiari i concetti introdotti sopra, ecco due esempi completi che illustrano le diverse fasi del ciclo di vita dei file 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();
Sicurezza e autorizzazioni
Il team di Chromium ha progettato e implementato l'API Storage Foundation utilizzando i principi fondamentali definiti in Controlling Access to Powerful Web Platform Features, tra cui controllo utente, trasparenza ed ergonomia.
Seguendo lo stesso pattern di altre moderne API di archiviazione 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, è limitato ai contesti sicuri.
Controllo utente
La quota di spazio di archiviazione verrà utilizzata per distribuire l'accesso allo spazio su disco e per prevenire abusi. 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
- Spiegazione pubblica
- Bug di monitoraggio di Chromium
- Voce di ChromeStatus.com
- Componente Blink:
Blink>Storage>NativeIO
- TAG Review
- Intent to Prototype
- Thread WebKit
- Thread di Mozilla
Ringraziamenti
L'API Storage Foundation è stata specificata e implementata da Emanuel Krivoy e Richard Stotz. Questo articolo è stato rivisto da Pete LePage e Joe Medley.
Immagine promozionale di Markus Spiske su Unsplash.