Questo case study illustra in che modo Kiwix, un'organizzazione non profit, utilizza la tecnologia delle app web progressive e l'API File System Access per consentire agli utenti di scaricare e archiviare archivi di internet di grandi dimensioni per l'utilizzo offline. Scopri di più sull'implementazione tecnica del codice relativo al sistema di file privato di Origin (OPFS), una nuova funzionalità del browser all'interno della PWA Kiwix che migliora la gestione dei file, offrendo un accesso migliorato agli archivi senza richieste di autorizzazione. L'articolo discute le sfide e mette in evidenza i potenziali sviluppi futuri di questo nuovo sistema di file.
Informazioni su Kiwix
Più di 30 anni dopo la nascita del web, un terzo della popolazione mondiale è ancora in attesa di un accesso affidabile a internet secondo l'Unione internazionale delle telecomunicazioni. È qui che termina la storia? Ovviamente no. Il team di Kiwix, un'organizzazione non profit con sede in Svizzera, ha sviluppato un ecosistema di app e contenuti open source che ha lo scopo di mettere a disposizione la conoscenza a persone con accesso limitato o nullo a internet. L'idea è che se tu non riesci ad accedere facilmente a internet, qualcuno può scaricare risorse chiave per te, dove e quando è disponibile la connettività, e archiviarle localmente per un uso offline successivo. Ora molti siti importanti, ad esempio Wikipedia, Project Gutenberg, Stack Exchange o persino i talk di TED, possono essere convertiti in archivi altamente compressi, chiamati file ZIM, e letti al volo dal browser Kiwix.
Gli archivi ZIM utilizzano la compressione Zstandard (ZSTD) altamente efficiente (le versioni precedenti utilizzavano XZ), principalmente per memorizzare HTML, JavaScript e CSS, mentre le immagini vengono solitamente convertite in formato WebP compresso. Ogni ZIM include anche un URL e un indice dei titoli. La compressione è fondamentale, poiché l'intera Wikipedia in inglese (6,4 milioni di articoli, più immagini) viene compressa a 97 GB dopo la conversione al formato ZIM, che sembra molto finché non lo fai rendersi conto che la somma di tutte le conoscenze umane ora può essere inserita in uno smartphone Android di media gamma. Sono disponibili anche molte risorse più piccole, tra cui versioni tematiche di Wikipedia, come matematica, medicina e così via.
Kiwix offre una gamma di app native destinate all'utilizzo su computer (Windows/Linux/macOS) e dispositivi mobili (iOS/Android). Questo studi di caso, tuttavia, si concentrerà sull'app web progressiva (PWA) che ha lo scopo di essere una soluzione universale e semplice per qualsiasi dispositivo con un browser moderno.
Esamineremo le sfide poste dallo sviluppo di un'app web universale che deve fornire accesso rapido a archivi di contenuti di grandi dimensioni completamente offline e alcune API JavaScript moderne, in particolare l'API File System Access e il Origin Private File System, che forniscono soluzioni innovative e interessanti a queste sfide.
Un'app web per l'utilizzo offline?
Gli utenti di Kiwix sono un gruppo eterogeneo con esigenze molto diverse e Kiwix ha poco o nessun controllo sui dispositivi e sui sistemi operativi su cui accederanno ai contenuti. Alcuni di questi dispositivi potrebbero essere lenti o obsoleti, soprattutto nelle aree a basso reddito del mondo. Sebbene Kiwix cerchi di coprire il maggior numero possibile di casi d'uso, l'organizzazione ha anche capito che poteva raggiungere ancora più utenti utilizzando il software più universale su qualsiasi dispositivo: il browser web. Così, ispirati dalla legge di Atwood, che afferma che qualsiasi applicazione che può essere scritta in JavaScript, verrà infine scritta in JavaScript, circa 10 anni fa alcuni sviluppatori di Kiwix si sono messi al lavoro per eseguire il porting del software Kiwix da C++ a JavaScript.
La prima versione di questa porta, chiamata Kiwix HTML5, era destinata all'ormai non più esistente Firefox OS e alle estensioni del browser. Al suo interno era (ed è) un motore di decompressione C++ (XZ e ZSTD) compilato nel linguaggio JavaScript intermedio di ASM.js e in seguito in Wasm o WebAssembly, utilizzando il compilatore Emscripten. In seguito ribattezzate Kiwix JS, le estensioni del browser sono ancora in fase di sviluppo attivo.
Accedi all'app web progressiva (PWA). Rendendosi conto del potenziale di questa tecnologia, gli sviluppatori di Kiwix hanno creato una versione PWA dedicata di Kiwix JS e si sono adoperati per aggiungere integrazioni del sistema operativo che consentissero all'app di offrire funzionalità simili a quelle native, in particolare per quanto riguarda l'utilizzo offline, l'installazione, la gestione dei file e l'accesso al file system.
Le PWA offline-first sono estremamente leggere e quindi perfette per contesti in cui la connessione a internet mobile è intermittente o costosa. La tecnologia alla base di questo è l'API Service Worker e la relativa API Cache, utilizzata da tutte le app basate su Kiwix JS. Queste API consentono alle app di agire come server, intercettando le richieste di recupero dal documento o dall'articolo principale visualizzato e reindirizzandole al backend (JS) per estrarre e creare una risposta dall'archivio ZIM.
Spazio di archiviazione ovunque
Date le grandi dimensioni degli archivi ZIM, lo spazio di archiviazione e l'accesso, soprattutto su dispositivi mobili, sono probabilmente il problema più grande per gli sviluppatori di Kiwix. Molti Utenti finali di Kiwix scaricano i contenuti in-app, quando è disponibile internet, per usarli successivamente offline. Altri utenti scaricano i contenuti su un PC utilizzando un torrent, poi li trasferiscono su un dispositivo mobile o tablet e alcuni scambiano contenuti su chiavette USB o hard disk portatili in aree con una connessione a internet mobile intermittente o costosa. Tutti questi modi di accedere ai contenuti da posizioni arbitrarie accessibili agli utenti devono essere supportati da Kiwix JS e Kiwix PWA.
Inizialmente, è stata l'API File a consentire a Kiwix JS di leggere archivi enormi di centinaia di GB (uno dei nostri archivi ZIM è di 166 GB) anche su dispositivi con poca memoria. Questa API è supportata universalmente in qualsiasi browser, anche in quelli molto vecchi, pertanto funge da opzione di riserva universale quando le API più recenti non sono supportate. È facile come definire un elemento input
in HTML, nel caso di Kiwix:
<input
type="file"
accept="application/octet-stream,.zim,.zimaa,.zimab,.zimac, ..."
value="Select folder with ZIM files"
id="archiveFilesLegacy"
multiple
/>
Una volta selezionato, l'elemento di input contiene gli oggetti File, che sono essenzialmente metadati che fanno riferimento ai dati sottostanti archiviati. Tecnicamente, il backend object-oriented di Kiwix, scritto in JavaScript lato client puro, legge piccole sezioni dell'ampio archivio in base alle necessità. Se questi slice devono essere decompressi, il backend li passa allo scompressore Wasm, ottenendo ulteriori slice, se richiesti, fino a quando non viene decompreso un blob completo (di solito un articolo o un asset). Ciò significa che l'archivio di grandi dimensioni non deve mai essere letto interamente nella memoria.
L'API File, pur essendo universale, ha uno svantaggio che ha fatto apparire le app Kiwix JS come scomode e antiquate rispetto alle app native: richiede all'utente di selezionare gli archivi utilizzando un selettore di file o di trascinare un file nell'app ogni volta che l'app viene lanciata, perché con questa API non è possibile mantenere le autorizzazioni di accesso da una sessione all'altra.
Per mitigare questa scarsa UX, come molti sviluppatori, gli sviluppatori di Kiwix JS hanno inizialmente scelto la strada di Electron. ElectronJS è un fantastico framework che offre funzionalità potenti, tra cui l'accesso completo al sistema di file utilizzando le API Node. Tuttavia, presenta alcuni svantaggi ben noti:
- Funziona solo su sistemi operativi desktop.
- È grande e pesante (70-100 MB).
Le dimensioni delle app Electron, dovute al fatto che in ogni app è inclusa una copia completa di Chromium, sono molto svantaggiose rispetto ai soli 5,1 MB della PWA ridotta a icona e in bundle.
Quindi, c'era un modo in cui Kiwix poteva migliorare la situazione per gli utenti della PWA?
L'API File System Access accorre in soccorso
Intorno al 2019, Kiwix ha appreso dell'esistenza di un'API emergente in fase di sperimentazione in Chrome 78, chiamata API Native File System. Prometteva la possibilità di ottenere un handle file per un file o una cartella e di archiviarlo in un database IndexedDB. È fondamentale che questo handle persista tra le sessioni dell'app, in modo che l'utente non debba scegliere di nuovo il file o la cartella quando riavvia l'app (anche se deve rispondere a una breve richiesta di autorizzazione). Quando è stata introdotta in produzione, è stata rinominata API File System Access e le parti di base sono state standardizzate da WHATWG come API File System (FSA).
Come funziona la parte dell'API relativa all'accesso al file system? Ecco alcuni punti importanti da tenere a mente:
- Si tratta di un'API asincrona (tranne per le funzioni specializzate nei Web Worker).
- I selettori di file o directory devono essere avviati tramite programmazione acquisendo un gesto dell'utente (fare clic o toccare un elemento dell'interfaccia utente).
- Affinché l'utente possa concedere di nuovo l'autorizzazione per accedere a un file selezionato in precedenza (in una nuova sessione), è necessario anche un gesto dell'utente. Infatti, il browser rifiuterà di mostrare la richiesta di autorizzazione se non viene avviata da un gesto dell'utente.
Il codice è relativamente semplice, a parte il fatto che devi utilizzare la complicata API IndexedDB per memorizzare gli handle di file e directory. La buona notizia è che esistono alcune librerie che svolgono gran parte del lavoro per te, come browser-fs-access. In Kiwix JS abbiamo deciso di lavorare direttamente con le API, che sono molto ben documentate.
Apertura dei selettori di file e directory
L'apertura di un selettore di file ha un aspetto simile a questo (qui vengono utilizzate le promesse, ma se preferisci async/await
sugar, consulta il tutorial di Chrome per gli sviluppatori):
return window
.showOpenFilePicker({ multiple: false })
.then(function (fileHandles) {
return processFileHandle(fileHandles[0]);
})
.catch(function (err) {
// This is normal if app is launching
console.warn(
'User cancelled, or cannot access fs without user gesture',
err,
);
});
Tieni presente che, per semplicità, questo codice elabora solo il primo
file selezionato (e vieta di selezionarne più di uno). Se vuoi consentire di selezionare più file con { multiple: true }
, devi semplicemente racchiudere tutte le promesse che elaborano ogni handle in un'istruzione Promise.all().then(...)
, ad esempio:
let promisesForFiles = fileHandles.map(function (fileHandle) {
return processFileHandle(fileHandle);
});
return Promise.all(promisesForFiles).then(function (arrayOfFiles) {
// Do something with the files array
console.log(arrayOfFiles);
}).catch(function (err) {
// Handle any errors that occurred during processing
console.error('Error processing file handles!', err);
)};
Tuttavia, è preferibile scegliere più file chiedendo all'utente di selezionare la directory contenente i file anziché i singoli file al suo interno, soprattutto perché gli utenti di Kiwix tendono a organizzare tutti i file ZIM nella stessa directory. Il codice per avviare il selettore di directory è quasi uguale a quello riportato sopra, tranne per il fatto che devi utilizzare window.showDirectoryPicker.then(function (dirHandle) { … });
.
Elaborazione dell'handle del file o della directory
Una volta ottenuto l'handle, devi elaborarlo, quindi la funzione
processFileHandle
potrebbe avere il seguente aspetto:
function processFileHandle(fileHandle) {
// Serialize fileHandle to indexedDB
serializeFSHandletoIdxDB('pickedFSHandle', fileHandle, function (val) {
console.debug('IndexedDB responded with ' + val);
});
return fileHandle.getFile().then(function (file) {
// Do something with the file
return file;
});
}
Tieni presente che devi fornire la funzione per archiviare l'handle del file. Non esistono metodi di utilità per questo, a meno che non utilizzi una libreria di astrazione. L'implementazione di Kiwix è visibile nel file cache.js
, ma potrebbe essere semplificata notevolmente se viene utilizzata solo per memorizzare e recuperare un handle di file o cartella.
L'elaborazione delle directory è un po' più complicata perché devi eseguire un'iterazione tra le voci della directory selezionata con entries.next()
asincrono per trovare i file o i tipi di file che ti interessano. Esistono vari modi per farlo, ma
questo è il codice utilizzato in Kiwix PWA, in outline:
let iterableEntryList = dirHandle.entries();
return iterateAsyncDirEntries(iterableEntryList, []).then(function (entryList) {
// Do something with the entry list
return entryList;
});
/**
* Iterates FileSystemDirectoryHandle iterator and adds entries to an array
* @param {Iterator} entries An asynchronous iterator of entries
* @param {Array} archives An array to which to add the entries (may be empty)
* @return {Promise<Array>} A Promise for an array of entries in the directory
*/
function iterateAsyncDirEntries(entries, archives) {
return entries
.next()
.then(function (result) {
if (!result.done) {
let entry = result.value[1];
// Filter for the files you want
if (/\.zim(\w\w)?$/i.test(entry.name)) {
archives.push(entry);
}
return iterateAsyncDirEntryArray(entries, archives);
} else {
// We've processed all the entries
if (!archives.length) {
console.warn('No archives found in the picked directory!');
}
return archives;
}
})
.catch(function (err) {
console.error('There was an error processing the directory!', err);
});
}
Tieni presente che per ogni voce in entryList
, in un secondo momento dovrai recuperare il file con entry.getFile().then(function (file) { … })
quando dovrai utilizzarlo o l'equivalente utilizzando const file = await entry.getFile()
in un async function
.
Possiamo fare di più?
Il requisito per l'utente di concedere l'autorizzazione avviato con un gesto dell'utente ai successivi lanci dell'app aggiunge una piccola frizione all'apertura (di nuovo) di file e cartelle, ma è comunque molto più fluido rispetto all'obbligo di scegliere di nuovo un file. Al momento gli sviluppatori di Chromium stanno finalizzando il codice che consentirebbe le autorizzazioni permanenti per le PWA installate. Si tratta di una funzionalità molto richiesta da molti sviluppatori di PWA ed è molto attesa.
Ma se non dovessimo aspettare?! Di recente, gli sviluppatori di Kiwix hanno scoperto che è possibile eliminare tutte le richieste di autorizzazione in questo momento utilizzando una nuova funzionalità dell'API File Access supportata sia dai browser Chromium che Firefox (e parzialmente da Safari, ma ancora manca FileSystemWritableFileStream
). Questa nuova funzionalità è il file system privato di Origin.
Passaggio a un sistema completamente nativo: il file system privato di Origin
Il Origin Private File System (OPFS) è ancora una funzionalità sperimentale nella PWA Kiwix, ma il team è davvero entusiasta di incoraggiare gli utenti a provarlo perché colma in gran parte il divario tra le app native e le app web. Ecco i principali vantaggi:
- È possibile accedere agli archivi nell'OPFS senza richieste di autorizzazione, anche al primo avvio. Gli utenti possono riprendere la lettura di un articolo e la navigazione in un archivio dal punto in cui avevano interrotto in una sessione precedente, senza alcuna difficoltà.
- Offre accesso altamente ottimizzato ai file archiviati al suo interno: su Android abbiamo riscontrato miglioramenti in termini di velocità da cinque a dieci volte.
L'accesso ai file standard in Android tramite l'API File è estremamente lento, soprattutto (come spesso accade per gli utenti di Kiwix) se gli archivi di grandi dimensioni sono archiviati su una scheda microSD anziché nello spazio di archiviazione del dispositivo. Tutto cambia con questa nuova API. Sebbene la maggior parte degli utenti non possa archiviare un file di 97 GB nell'OPFS (che consuma lo spazio di archiviazione del dispositivo, non della scheda microSD), è perfetto per archiviare archivi di piccole e medie dimensioni. Vuoi la enciclopedia medica più completa di WikiProject Medicina? Nessun problema, con 1,7 GB si adatta facilmente all'OPFS. (Suggerimento: cerca other → mdwiki_en_all_maxi nella raccolta in-app.)
Come funziona l'OPFS
L'OPFS è un file system fornito dal browser, separato per ogni origine, che può essere considerato simile allo spazio di archiviazione basato sulle app su Android. I file possono essere importati nell'OPFS dal file system visibile all'utente o possono essere scaricati direttamente al suo interno (l'API consente anche di creare file nell'OPFS). Una volta nell'OPFS, vengono isolate dal resto del dispositivo. Sui browser desktop basati su Chromium, è anche possibile esportare nuovamente i file dall'OPFS al file system visibile dall'utente.
Per utilizzare il sistema Origin Private File System (OPFS), il primo passaggio consiste nel richiederne l'accesso utilizzando navigator.storage.getDirectory()
(di nuovo, se preferisci vedere il codice che utilizza navigator.storage.getDirectory()
, leggi Origin Private File System):await
return navigator.storage
.getDirectory()
.then(function (handle) {
return processDirHandle(handle);
})
.catch(function (err) {
console.warn('Unable to get the OPFS directory entry', err);
});
L'handle che ottieni è dello stesso tipo di FileSystemDirectoryHandle
che ottieni da window.showDirectoryPicker()
citato sopra, il che significa che puoi riutilizzare il codice che lo gestisce (e fortunatamente non è necessario memorizzarlo in indexedDB
: recuperalo quando ti serve). Supponiamo che tu abbia già alcuni file nell'OPFS e che tu voglia utilizzarli. Quindi, utilizzando la funzione iterateAsyncDirEntries()
mostrata in precedenza, potresti fare qualcosa di simile:
return navigator.storage.getDirectory().then(function (dirHandle) {
let entries = dirHandle.entries();
return iterateAsyncDirEntries(entries, [])
.then(function (archiveList) {
return archiveList;
})
.catch(function (err) {
console.error('Unable to iterate OPFS entries', err);
});
});
Non dimenticare che devi comunque utilizzare getFile()
su qualsiasi voce con cui vuoi lavorare dall'array archiveList
.
Importazione di file nell'OPFS
Come faccio a inserire i file nell'OPFS? Non così in fretta. Innanzitutto, devi stimare la quantità di spazio di archiviazione di cui hai bisogno e assicurarti che gli utenti non provino a inserire un file di 97 GB se non è possibile.
Ottenere la quota stimata è facile:
navigator.storage.estimate().then(function (estimate) { … });
. È leggermente più difficile capire come mostrarlo all'utente. Nell'app Kiwix, abbiamo optato per un piccolo riquadro in-app visibile accanto alla casella di controllo che consente agli utenti di provare il formato OPFS:
Il riquadro viene compilato utilizzando
estimate.quota
e
estimate.usage
,
ad esempio:
let OPFSQuota; // Global variable, so we don't have to keep checking it
return navigator.storage.estimate().then(function (estimate) {
const percent = ((estimate.usage / estimate.quota) * 100).toFixed(2);
OPFSQuota = estimate.quota - estimate.usage;
document.getElementById('OPFSQuota').innerHTML =
'<b>OPFS storage quota:</b><br />Used: <b>' +
percent +
'%</b>; ' +
'Remaining: <b>' +
(OPFSQuota / 1024 / 1024 / 1024).toFixed(2) +
' GB</b>';
});
Come puoi vedere, è presente anche un pulsante che consente agli utenti di aggiungere file all'OPFS dal
file system visibile all'utente. La buona notizia è che puoi semplicemente utilizzare
l'API File per ottenere
l'oggetto File (o gli oggetti File) necessari da importare. In effetti, è importante non utilizzare window.showOpenFilePicker()
perché questo metodo non è supportato da Firefox, mentre OPFS è supportato in modo molto chiaro.
Il pulsante Aggiungi file visibile nello screenshot sopra non è un selettore di file precedente, ma click()
un selettore precedente nascosto (elemento <input type="file" multiple … />
) quando viene fatto clic o toccato. L'app acquisisce quindi l'evento change
dell'input del file nascosto, controlla le dimensioni dei file e li rifiuta se sono troppo grandi per la quota. Se tutto va bene, chiedi all'utente se vuole aggiungerli:
archiveFilesLegacy.addEventListener('change', function (files) {
const filesArray = Array.from(files.target.files);
// Abort if user didn't select any files
if (filesArray.length === 0) return;
// Calculate the size of the picked files
let filesSize = 0;
filesArray.forEach(function (file) {
filesSize += file.size;
});
// Check the size of the files does not exceed the quota
if (filesSize > OPFSQuota) {
// Oh no, files are too big! Tell user...
console.log('Files would exceed the OPFS quota!');
} else {
// Ask user if they're sure... if user said yes...
return importOPFSEntries(filesArray)
.then(function () {
// Tell user we successfully imported the archives
})
.catch(function (err) {
// Tell user there was an error (error catching is important!)
});
}
});
Poiché su alcuni sistemi operativi, come Android, l'importazione degli archivi non è un'operazione molto rapida, Kiwix mostra anche un banner e un piccolo cursore durante l'importazione degli archivi. Il team non ha capito come aggiungere un indicatore di avanzamento per questa operazione: se ci riesci, rispondi su una cartolina.
Come ha fatto Kiwix a implementare la funzione importOPFSEntries()
? Ciò comporta
l'utilizzo del metodo fileHandle.createWriteable()
, che consente di eseguire lo streaming di ciascun
file nell'OPFS. Il browser si occupa di tutto il lavoro. (Kiwix utilizza le promesse per motivi legati al nostro codebase legacy, ma va detto che in questo caso await
produce una sintassi più semplice ed evita l'effetto piramide del destino.)
function importOPFSEntries(files) {
// Get a handle on the OPFS directory
return navigator.storage
.getDirectory()
.then(function (dir) {
// Collect the promises for each file that we want to write
let promises = files.map(function (file) {
// Create the file and get a writeable handle on it
return dir
.getFileHandle(file.name, { create: true })
.then(function (fileHandle) {
// Get a writer for the file
return fileHandle.createWritable().then(function (writer) {
// Show a banner / spinner, then write the file
return writer
.write(file)
.then(function () {
// Finished with this writer
return writer.close();
})
.catch(function (err) {
console.error('There was an error writing to the OPFS!', err);
});
});
})
.catch(function (err) {
console.error('Unable to get file handle from OPFS!', err);
});
});
// Return a promise that resolves when all the files have been written
return Promise.all(promises);
})
.catch(function (err) {
console.error('Unable to get a handle on the OPFS directory!', err);
});
}
Scaricare uno stream di file direttamente nell'OPFS
Una variante di questa operazione è la possibilità di riprodurre in streaming un file da internet direttamente
nel file system OPFS o in qualsiasi directory per la quale disponi di un handle della directory (ovvero, le directory selezionate con window.showDirectoryPicker()
). Utilizza gli stessi
principi del codice riportato sopra, ma crea un Response
costituito da un
ReadableStream
e un controller che mette in coda i byte letti dal
file remoto. Il Response.body
risultante viene poi incanalato allo scrittore del nuovo file all'interno dell'OPFS.
In questo caso, Kiwix è in grado di conteggiare i byte che passano attraverso il
ReadableStream
, fornendo così all'utente un indicatore di avanzamento e avvertendolo anche di non uscire dall'app durante il download. Il codice è un po' troppo complicato per essere mostrato qui, ma poiché la nostra app è un'app FOSS, puoi esaminare il codice sorgente se ti interessa fare qualcosa di simile. Ecco come appare l'interfaccia utente di Kiwix (i diversi valori di avanzamento mostrati di seguito sono dovuti al fatto che il banner viene aggiornato solo quando la percentuale cambia, ma il riquadro Avanzamento download viene aggiornato più regolarmente):
Poiché il download può essere un'operazione piuttosto lunga, Kiwix consente agli utenti di utilizzare liberamente l'app durante l'operazione, ma garantisce che il banner venga sempre visualizzato, in modo che gli utenti non dimentichino di chiudere l'app fino al completamento dell'operazione di download.
Implementazione di un mini gestore di file in-app
A questo punto, gli sviluppatori della PWA Kiwix hanno capito che non è sufficiente poter aggiungere file all'OPFS. L'app doveva anche offrire agli utenti un modo per eliminare i file che non sono più necessari da questa area di archiviazione ed, idealmente, anche per esportare i file bloccati nel file system visibile all'utente nel file system visibile all'utente. Di fatto, è stato necessario implementare un mini sistema di gestione dei file all'interno dell'app.
Un breve cenno all'favolosa estensione OPFS Explorer per Chrome (funziona anche in Edge). Aggiunge una scheda negli Strumenti per sviluppatori che ti consente di vedere esattamente cosa è presente nell'OPFS ed eliminare anche i file non validi o con errori. È stato indispensabile per verificare il funzionamento del codice, monitorare il comportamento dei download e, in generale, per mettere in ordine i nostri esperimenti di sviluppo.
L'esportazione dei file dipende dalla possibilità di ottenere un handle file su un file o una directory selezionati in cui Kiwix salverà il file esportato, pertanto funziona solo nei contesti in cui può utilizzare il metodo window.showSaveFilePicker()
. Se
i file Kiwix fossero più piccoli di diversi GB, potremmo creare un blob
in memoria, assegnargli un URL e scaricarlo nel file system visibile all'utente.
Purtroppo, non è possibile con archivi così grandi. Se supportata,
l'esportazione è abbastanza semplice: è praticamente la stessa cosa, al contrario, del salvataggio di un
file nell'OPFS (ottieni un handle del file da salvare, chiedi all'utente di scegliere una
posizione in cui salvarlo con window.showSaveFilePicker()
, quindi utilizza
createWriteable()
su saveHandle
). Puoi
visualizzare il codice
nel repository.
L'eliminazione dei file è supportata da tutti i browser e può essere eseguita con un semplice dirHandle.removeEntry('filename')
. Nel caso di Kiwix, abbiamo preferito eseguire l'iterazione delle voci OPFS come abbiamo fatto sopra, in modo da verificare prima che il file selezionato esista e chiedere conferma, ma potrebbe non essere necessario per tutti. Se ti interessa, puoi anche esaminare il nostro codice.
È stato deciso di non appesantire l'interfaccia utente di Kiwix con pulsanti che offrono queste opzioni, ma di posizionare piccole icone direttamente sotto l'elenco dell'archivio. Se tocchi una di queste icone, il colore dell'elenco degli archivi cambia, dando all'utente un indizio visivo su cosa sta per fare. L'utente fa clic o tocca uno degli archivi e viene eseguita l'operazione corrispondente (esportazione o eliminazione) (dopo la conferma).
Infine, ecco una demo in screencast di tutte le funzionalità di gestione dei file discusse sopra: aggiunta di un file al file system di OpenProject, download diretto di un file al suo interno, eliminazione di un file ed esportazione nel file system visibile all'utente.
Il lavoro di uno sviluppatore non finisce mai
OPFS è un'ottima innovazione per gli sviluppatori di PWA, in quanto offre funzionalità di gestione dei file molto potenti che contribuiscono notevolmente a colmare il divario tra le app native e le app web. Ma gli sviluppatori sono un gruppo scontento: non sono mai abbastanza soddisfatti. OPFS è quasi perfetto, ma non del tutto… È fantastico che le funzionalità principali funzionino sia nei browser Chromium che in Firefox e che siano implementate su Android e computer. Ci auguriamo che l'intero insieme di funzionalità possa essere implementato anche su Safari e iOS a breve. Rimangono i seguenti problemi:
- Attualmente Firefox applica un limite di 10 GB alla quota OPFS, indipendentemente dallo spazio di archiviazione di base disponibile. Sebbene per la maggior parte degli autori di PWA questa quantità possa essere sufficiente, per Kiwix è piuttosto restrittiva. Fortunatamente, i browser Chromium sono molto più generosi.
- Al momento non è possibile esportare file di grandi dimensioni dall'OPFS al
sistema file visibile dall'utente sui browser mobile o su Firefox per computer, perché
window.showSaveFilePicker()
non è implementato. In questi browser, i file di grandi dimensioni vengono effettivamente intrappolati nell'OPFS. Ciò va contro lo spirito di Kiwix, che si basa sull'accesso aperto ai contenuti e sulla possibilità di condividere gli archivi tra gli utenti, soprattutto nelle aree con connettività a internet intermittente o costosa. - Gli utenti non possono controllare lo spazio di archiviazione che verrà utilizzato dal file system virtuale OPFS. Questo è particolarmente problematico sui dispositivi mobili, in cui gli utenti possono avere a disposizione molto spazio su una scheda microSD, ma pochissimo nello spazio di archiviazione del dispositivo.
Ma, tutto sommato, si tratta di piccoli problemi in un contesto che rappresenta un enorme passo avanti per l'accesso ai file nelle PWA. Il team di Kiwix PWA è molto grato agli sviluppatori e ai sostenitori di Chromium che hanno proposto e progettato per primi l'API File System Access e per il duro lavoro di raggiungere il consenso tra i fornitori di browser sull'importanza del file system privato dell'origine. Per la PWA Kiwix JS, ha risolto moltissimi dei problemi di UX che hanno rallentato l'app in passato e ci aiuta nel nostro intento di migliorare l'accessibilità dei contenuti di Kiwix per tutti. Prova la PWA Kiwix e comunica agli sviluppatori cosa ne pensi.
Per alcune ottime risorse sulle funzionalità delle PWA, dai un'occhiata a questi siti:
- Project Fugu API showcase: una raccolta di app web che mostrano funzionalità che colmano il divario tra app native e PWA.
- Che cosa è possibile fare oggi con le PWA: una presentazione di ciò che è possibile fare oggi con le PWA.