Aller au contenu
HexaTransfer
Retour au blog
Approfondissements techniques

Navigateur Stockage APIs pour Transfert de fichiers Applications

Exploitez IndexedDB, File System Access API, et Cache API pour fichier transfer apps. Stockage limits, performance, et browser compatibility.

Le stockage navigateur pour une application de transfert de fichiers se divise clairement en quatre APIs : IndexedDB pour les métadonnées de session et de fragments structurées (transactionnel, asynchrone, typé), la File System Access API pour lire et écrire des fichiers multi-gigaoctets directement sur le disque de l'utilisateur, la Cache API pour les réponses HTTP et les assets du shell applicatif, et la Storage Manager API pour la gestion des quotas et les indications de persistance. L'Origin Private File System (OPFS) complète cet ensemble pour des opérations d'entrée/sortie haute performance dans un bac à sable. Choisir la bonne API est déterminant, car les limites vont de 1 Go sur iOS Safari à « 60 % du disque libre » sur Chrome desktop — le mauvais choix conduit inévitablement à une QuotaExceededError en production.

Associer chaque API au bon usage

Utilisez IndexedDB pour tout ce qui ressemble à une ligne de données : sessions d'envoi, index de fragments complétés, métadonnées de partage, tokens de révocation. Il est asynchrone, transactionnel, indexable, et survit aux sessions.

Utilisez la File System Access API quand vous devez écrire des octets sur disque sans charger l'intégralité du fichier en RAM — idéal pour sauvegarder des téléchargements déchiffrés de plus de 500 Mo. Chrome, Edge et Opera la supportent ; Firefox et Safari n'implémentent qu'un sous-ensemble en lecture seule via showOpenFilePicker.

Utilisez la Cache API pour les objets Response HTTP — votre bundle JS, CSS, icônes, et éventuellement les réponses API en cache. Elle est optimisée pour l'interception fetch dans les Service Workers.

Utilisez OPFS (une branche Origin-Private de la File System Access API) quand vous voulez un stockage rapide, isolé, non visible par l'utilisateur — par exemple un tampon d'écriture pendant une passe de chiffrement multi-gigaoctets. Il atteint un débit disque un ordre de grandeur supérieur à IndexedDB pour les blobs binaires.

IndexedDB sans les aspérités

L'API brute IndexedDB est notoirement maladroite avec sa gestion par événements. Utilisez le package idb de Jake Archibald (1,5 Ko gzippé) ou Dexie.js (20 Ko, API de requête plus riche) :

import { openDB } from 'idb';
const db = await openDB('transfers', 2, {
  upgrade(db, oldVersion) {
    if (oldVersion < 1) {
      const sessions = db.createObjectStore('sessions', { keyPath: 'id' });
      sessions.createIndex('by_expiry', 'expiresAt');
    }
    if (oldVersion < 2) {
      db.createObjectStore('chunks', { keyPath: ['sessionId', 'index'] });
    }
  }
});
await db.put('sessions', { id: 'abc', fileName: 'rapport.pdf', expiresAt: Date.now() + 86400000 });

Les migrations de version s'exécutent dans le callback upgrade. Gardez toujours les migrations protégées par oldVersion pour que les utilisateurs passant de v1 à v3 reçoivent les deux étapes.

IndexedDB gère la plupart des formes de données, y compris les Blobs et les références de File, via le clone structuré. Cela signifie que vous pouvez stocker un handle File dans un enregistrement de session et relire les octets du fichier d'origine après un rechargement d'onglet — idéal pour les envois reprenables.

File System Access API pour les grands téléchargements

L'API vous permet de remettre un flux en écriture à la boîte de dialogue de sauvegarde du navigateur :

const handle = await window.showSaveFilePicker({
  suggestedName: 'archive-dechiffree.zip',
  types: [{ description: 'Zip', accept: { 'application/zip': ['.zip'] } }]
});
const writable = await handle.createWritable();
await decryptionStream.pipeTo(writable);

Les octets vont directement sur disque, sans jamais passer par le tas JS. C'est la seule manière pratique de sauvegarder un fichier déchiffré de 10 Go dans le navigateur.

Pour Firefox et Safari, repliez-vous sur StreamSaver.js, qui utilise un Service Worker pour synthétiser une réponse en streaming qui déclenche l'interface de téléchargement. Mêmes ergonomies, légèrement plus de composants.

Les handles de fichiers persistants permettent également à une application de rouvrir des fichiers entre sessions. Une fois que l'utilisateur a accordé la permission via showOpenFilePicker, vous pouvez conserver le FileSystemFileHandle dans IndexedDB et appeler ultérieurement handle.requestPermission() pour récupérer l'accès sans redemander pour chaque fichier.

OPFS pour l'espace de travail temporaire

L'Origin Private File System est un stockage isolé par origine qui se comporte comme un système de fichiers mais n'est pas visible par l'utilisateur :

const root = await navigator.storage.getDirectory();
const fh = await root.getFileHandle('scratch.bin', { create: true });
const access = await fh.createSyncAccessHandle(); // workers uniquement
access.write(buffer, { at: offset });
access.flush();
access.close();

createSyncAccessHandle n'est disponible qu'à l'intérieur des Web Workers (y compris les Service Workers). Il est synchrone et extrêmement rapide — les benchmarks montrent des performances 3 à 10 fois supérieures à IndexedDB pour les écritures séquentielles. Utilisez-le pour tamponner quelques centaines de mégaoctets de sortie de chiffrement avant l'envoi, ou pour mettre en cache une copie de travail déchiffrée sans polluer le dossier Téléchargements de l'utilisateur.

Safari 17 a livré OPFS avec des handles d'accès synchrones ; Firefox 111 a suivi. Les trois principaux navigateurs le supportent désormais, ce qui en fait un choix viable pour le code de production.

Quotas de stockage et comment les survivre

Toutes les APIs partagent le même pool de quotas d'origine. Plafonds approximatifs :

  • Chrome desktop : 60 % du disque libre
  • Firefox desktop : 50 % du disque libre, plafonné à 2 Go par origine par défaut
  • Safari desktop : avertissement à 1 Go, peut atteindre ~20 % du disque avec approbation utilisateur
  • iOS Safari : 1 Go par origine, éviction agressive après 7 jours sans utilisation
  • Chrome Android : 10 % du disque libre, éviction sous pression

Vérifiez le quota à l'exécution :

const { quota, usage } = await navigator.storage.estimate();
console.log(`Utilisation : ${(usage/1e9).toFixed(2)} Go sur ${(quota/1e9).toFixed(2)} Go`);

Demandez la persistance pour les stores critiques :

const persisted = await navigator.storage.persist();

Retourne true si le navigateur a accordé le stockage persistant, signifiant qu'il ne sera pas évincé sous pression. Chrome l'accorde automatiquement aux sites avec lesquels l'utilisateur a significativement interagi ; Firefox demande confirmation.

Cache API pour le shell applicatif et le mode hors ligne

La Cache API stocke des paires Request + Response et est le bon choix à l'intérieur des Service Workers :

const cache = await caches.open('shell-v7');
await cache.addAll([
  '/', '/app.js', '/app.css', '/icons/192.png'
]);

Récupérez lors de l'interception :

self.addEventListener('fetch', (e) => {
  e.respondWith(caches.match(e.request).then(r => r ?? fetch(e.request)));
});

Ne mettez pas les octets de fichiers chiffrés dans la Cache API. Un objet Response de 2 Go dépasse d'un coup le quota d'iOS Safari et ne peut pas être récupéré par requêtes de plage par la suite. Les octets vont dans OPFS ou directement sur disque via File System Access.

Gérer l'éviction et la perte de données avec élégance

Le stockage non-persistant est évincé — vous devez le prévoir. iOS Safari évince après 7 jours sans utilisation, quel que soit le quota. Chrome n'évince que lorsque le disque est réellement sous pression. Firefox évince les origines les moins récemment utilisées une fois que le pool de quotas se remplit.

Deux schémas défensifs :

  • Écrivez tout état que vous ne pouvez pas recréer (identifiants de session d'envoi, décalages de fragments partiels) dans un format compatible avec le rechargement afin qu'une nouvelle page puisse re-fetcher depuis le serveur et continuer.
  • Pour les états de longue durée, demandez navigator.storage.persist() et affichez une interface utilisateur pour que les utilisateurs confirment quand le navigateur le demande.

Gardez le serveur comme source de vérité pour tout ce que vous ne pouvez pas vous permettre de perdre. Traitez le stockage navigateur comme un cache rapide qui peut disparaître du jour au lendemain.

Mines de compatibilité navigateur

Trois pièges reviennent régulièrement :

  1. indexedDB.databases() n'est pas supporté dans Firefox (les utilisateurs ayant opté pour « supprimer les cookies à la fermeture » perdent tout le contenu IndexedDB sans événements déclenchés).
  2. FileSystemFileHandle.queryPermission() se comporte différemment après rechargement — retourne parfois 'prompt' même quand la permission est accordée. Appelez toujours requestPermission() par précaution.
  3. Le mode privé / incognito donne à toutes les APIs un quota séparé, plus petit, valable uniquement pour la session. Du code qui fonctionne en navigation normale peut déclencher QuotaExceededError immédiatement en fenêtre privée.

HexaTransfer utilise IndexedDB pour l'état de session, OPFS pour la mise en tampon du texte chiffré pendant le chiffrement en streaming, et la File System Access API pour les téléchargements déchiffrés de 10 Go sur les navigateurs supportés. Essayez-le sur https://hexatransfer.com — gratuit, sans compte, 10 Go maximum.

Choisir une combinaison pour votre application

Pour la plupart des applications de transfert, la bonne combinaison est : le wrapper idb sur IndexedDB pour les métadonnées, les handles d'accès synchrones OPFS pour l'espace de chiffrement/déchiffrement, la Cache API pour le shell applicatif dans un Service Worker, la File System Access API pour les téléchargements finaux avec fallback StreamSaver, et un appel navigator.storage.persist() lors de l'onboarding. Cela couvre chaque navigateur livré aujourd'hui, reste sous les quotas sur mobile, et récupère gracieusement quand quelque chose est évincé. Construisez de petits adaptateurs autour de chaque API afin que le jour où OPFS propose une nouvelle méthode ou Safari augmente son quota, vous ne changiez qu'un fichier.

Envoyez vos fichiers volumineux en toute sécurité avec le chiffrement de bout en bout

Transférez des fichiers jusqu'à 10 Go gratuitement avec le chiffrement de bout en bout. Aucun compte requis. Vos fichiers sont chiffrés dans votre navigateur avant l'envoi — personne d'autre ne peut les lire.

Envoyer un fichier