Stockage pour le Web

Il existe de nombreuses options pour stocker des données dans le navigateur. Quelle option vous convient le mieux ?

Les connexions Internet peuvent être irrégulières ou inexistantes en déplacement. C'est pourquoi la compatibilité hors connexion et les performances fiables sont des fonctionnalités courantes des applications Web progressives. Même dans des environnements sans fil parfaits, un usage judicieux du cache et d'autres techniques de stockage peut considérablement améliorer l'expérience utilisateur. Il existe plusieurs façons de mettre en cache vos ressources d'application statiques (HTML, JavaScript, CSS, images, etc.) et vos données (données utilisateur, articles d'actualités, etc.). Mais quelle est la meilleure solution ? Combien pouvez-vous stocker ? Comment l'empêcher d'être supprimé ?

Voici une recommandation générale pour le stockage des ressources :

IndexedDB, OPFS et l'API Cache Storage sont compatibles avec tous les navigateurs modernes. Ils sont asynchrones et ne bloquent pas le thread principal (mais il existe également une variante synchrone de l'OPFS disponible exclusivement dans les web workers). Ils sont accessibles depuis l'objet window, les web workers et les service workers, ce qui vous permet de les utiliser n'importe où dans votre code.

Qu'en est-il des autres mécanismes de stockage ?

Plusieurs autres mécanismes de stockage sont disponibles dans le navigateur, mais ils sont limités et peuvent entraîner des problèmes de performances importants.

SessionStorage est spécifique à l'onglet et limité à la durée de vie de l'onglet. Elle peut être utile pour stocker de petites quantités d'informations spécifiques à une session, par exemple une clé IndexedDB. Il doit être utilisé avec précaution, car il est synchrone et bloque le thread principal. Il est limité à environ 5 Mo et ne peut contenir que des chaînes. Étant donné qu'il est spécifique à l'onglet, il n'est pas accessible depuis les web workers ni les service workers.

Évitez d'utiliser LocalStorage, car il est synchrone et bloque le thread principal. Il est limité à environ 5 Mo et ne peut contenir que des chaînes. LocalStorage n'est pas accessible depuis les nœuds de travail Web ni les nœuds de travail de service.

Les cookies ont une utilité, mais ils ne doivent pas être utilisés pour le stockage. Les cookies sont envoyés avec chaque requête HTTP. Par conséquent, le stockage d'une quantité supérieure à une petite quantité de données augmentera considérablement la taille de chaque requête Web. Ils sont synchrones et ne sont pas accessibles depuis les nœuds de calcul Web. Comme LocalStorage et SessionStorage, les cookies ne sont limités qu'aux chaînes.

L'API File System Access a été conçue pour permettre aux utilisateurs de lire et de modifier des fichiers sur leur système de fichiers local. L'utilisateur doit accorder une autorisation avant qu'une page puisse lire ou écrire dans un fichier local. Les autorisations ne sont pas conservées entre les sessions, sauf si un gestionnaire de fichiers est mis en cache dans IndexedDB. L'API File System Access est la plus adaptée aux cas d'utilisation tels que les éditeurs, où vous devez ouvrir un fichier, le modifier, puis éventuellement enregistrer les modifications apportées au fichier.

Les API File System et FileWriter fournissent des méthodes permettant de lire et d'écrire des fichiers dans un système de fichiers en bac à sable. Bien qu'il soit asynchrone, il n'est pas recommandé, car il n'est disponible que dans les navigateurs basés sur Chromium.

Combien de données puis-je stocker ?

En bref, beaucoup, au moins quelques centaines de mégaoctets, et potentiellement des centaines de gigaoctets ou plus. Les implémentations de navigateur varient, mais la quantité d'espace de stockage disponible est généralement basée sur la quantité d'espace de stockage disponible sur l'appareil.

  • Chrome autorise le navigateur à utiliser jusqu'à 80 % de l'espace disque total. Une origine peut utiliser jusqu'à 60 % de l'espace disque total. Vous pouvez utiliser l'API StorageManager pour déterminer le quota maximal disponible. Il est possible que les autres navigateurs basés sur Chromium soient différents.
    • En mode navigation privée, Chrome réduit la quantité de stockage qu'une origine peut utiliser à environ 5 % de l'espace disque total.
    • Si l'utilisateur a activé l'option "Effacer les cookies et les données des sites lorsque vous fermez toutes les fenêtres" dans Chrome, le quota de stockage est considérablement réduit à un maximum d'environ 300 Mo.
  • Avec Firefox, le navigateur peut utiliser jusqu'à 50% de l'espace disque disponible. Un groupe eTLD+1 (par exemple, example.com, www.example.com et foo.bar.example.com) peuvent utiliser jusqu'à 2 Go. Vous pouvez utiliser l'API StorageManager pour déterminer l'espace restant disponible.
  • Safari (sur ordinateur et mobile) semble autoriser environ 1 Go. Lorsque la limite est atteinte, Safari invite l'utilisateur à l'augmenter par incréments de 200 Mo. Je n'ai trouvé aucune documentation officielle à ce sujet.
    • Si une PWA est ajoutée à l'écran d'accueil dans Safari pour mobile, un nouveau conteneur de stockage est créé, et rien n'est partagé entre la PWA et Safari pour mobile. Une fois le quota atteint pour une PWA installée, il ne semble plus possible de demander de l'espace de stockage supplémentaire.

Auparavant, si un site dépassait un certain seuil de données stockées, le navigateur invitait l'utilisateur à autoriser l'utilisation de davantage de données. Par exemple, si l'origine utilisait plus de 50 Mo, le navigateur inviterait l'utilisateur à lui autoriser de stocker jusqu'à 100 Mo, puis à demander à nouveau par incréments de 50 Mo.

Aujourd'hui, la plupart des navigateurs récents n'invitent pas l'utilisateur et permettent à un site d'utiliser jusqu'à son quota alloué. L'exception semble être Safari, qui s'affiche lorsque le quota de stockage est dépassé, demandant l'autorisation d'augmenter le quota alloué. Si une origine tente d'utiliser plus que son quota alloué, les tentatives ultérieures d'écriture de données échoueront.

Comment connaître l'espace de stockage disponible ?

Dans de nombreux navigateurs, vous pouvez utiliser l'API StorageManager pour déterminer l'espace de stockage disponible pour l'origine et la quantité qu'elle utilise. Il indique le nombre total d'octets utilisés par IndexedDB et l'API Cache, et permet de calculer l'espace de stockage restant approximatif.

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

Vous devez détecter les erreurs de dépassement de quota (voir ci-dessous). Dans certains cas, il est possible que le quota disponible dépasse l'espace de stockage disponible.

Inspecter

Pendant le développement, vous pouvez utiliser les outils de développement de votre navigateur pour inspecter les différents types de stockage et effacer toutes les données stockées.

Une nouvelle fonctionnalité a été ajoutée dans Chrome 88 qui vous permet d'ignorer le quota de stockage du site dans le volet de stockage. Cette fonctionnalité vous permet de simuler différents appareils et de tester le comportement de vos applications dans des scénarios de faible disponibilité de disque. Accédez à Application, puis à Stockage, activez la case Simuler un quota de stockage personnalisé, puis saisissez un nombre valide pour simuler le quota de stockage.

En travaillant sur ce guide, j'ai écrit un outil simple pour essayer d'utiliser rapidement autant d'espace de stockage que possible. C'est un moyen rapide de tester différents mécanismes de stockage et de voir ce qui se passe lorsque vous utilisez la totalité de votre quota.

Que faire si vous dépassez le quota ?

Que devez-vous faire lorsque vous dépassez le quota ? Plus important encore, vous devez toujours détecter et gérer les erreurs d'écriture, qu'il s'agisse d'un QuotaExceededError ou d'un autre élément. Ensuite, en fonction de la conception de votre application, décidez comment la gérer. Par exemple, supprimez les contenus auxquels il n'a pas été accédé depuis longtemps, supprimez les données en fonction de leur taille ou permettez aux utilisateurs de choisir ce qu'ils souhaitent supprimer.

IndexedDB et l'API Cache génèrent une exception DOMError nommée QuotaExceededError lorsque vous avez dépassé le quota disponible.

IndexedDB

Si l'origine a dépassé son quota, les tentatives d'écriture dans IndexedDB échoueront. Le gestionnaire onabort() de la transaction est appelé, en transmettant un événement. L'événement inclut un DOMException dans la propriété d'erreur. La vérification de l'erreur name renvoie QuotaExceededError.

const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
  const error = event.target.error; // DOMException
  if (error.name == 'QuotaExceededError') {
    // Fallback code goes here
  }
};

API Cache

Si l'origine a dépassé son quota, les tentatives d'écriture dans l'API Cache seront refusées avec un DOMException QuotaExceededError.

try {
  const cache = await caches.open('my-cache');
  await cache.add(new Request('/sample1.jpg'));
} catch (err) {
  if (error.name === 'QuotaExceededError') {
    // Fallback code goes here
  }
}

Comment fonctionne l'éviction ?

Le stockage Web est divisé en deux buckets : "Meilleur effort" et "Persistant". "Mieux que possible" signifie que le stockage peut être effacé par le navigateur sans interrompre l'utilisateur, mais qu'il est moins durable pour les données critiques ou à long terme. L'espace de stockage persistant n'est pas automatiquement effacé lorsque l'espace disponible est faible. L'utilisateur doit effacer manuellement cet espace de stockage (via les paramètres du navigateur).

Par défaut, les données d'un site (y compris IndexedDB, l'API Cache, etc.) appartiennent à la catégorie "Meilleurs efforts". Cela signifie que, sauf si un site a demandé un stockage persistant, le navigateur peut supprimer les données du site à sa discrétion, par exemple lorsque l'espace de stockage de l'appareil est faible.

Pour éviter au mieux les situations, les règles d'éviction sont les suivantes:

  • Les navigateurs basés sur Chromium commencent à supprimer des données lorsque l'espace disponible est insuffisant. Ils effacent d'abord toutes les données du site de l'origine la moins récemment utilisée, puis de la suivante, jusqu'à ce que le navigateur ne dépasse plus la limite.
  • Firefox commence à supprimer des données lorsque l'espace disque disponible est saturé. Il efface d'abord toutes les données du site de l'origine la moins récemment utilisée, puis de la suivante, jusqu'à ce que le navigateur ne dépasse plus la limite.
  • Safari ne supprimait pas auparavant les données, mais a récemment mis en place une nouvelle limite de sept jours pour tout l'espace de stockage en écriture (voir ci-dessous).

À partir d'iOS et iPadOS 13.4 et de Safari 13.1 sur macOS, un plafond de sept jours est appliqué à tout stockage en écriture de script, y compris IndexedDB, l'enregistrement de service worker et l'API Cache. Cela signifie que Safari supprimera tout le contenu du cache au bout de sept jours d'utilisation de Safari si l'utilisateur n'interagit pas avec le site. Cette règle d'éviction ne s'applique pas aux PWA installés qui ont été ajoutés à l'écran d'accueil. Pour en savoir plus, consultez Blocage complet des cookies tiers et plus sur le blog WebKit.

Compartiments de stockage

L'objectif de base de l'API Storage Buckets est d'autoriser les sites à créer plusieurs buckets de stockage, dans lesquels le navigateur peut choisir de supprimer chaque bucket indépendamment des autres buckets. Cela permet aux développeurs de spécifier la priorité d'éviction pour s'assurer que les données les plus intéressantes ne sont pas supprimées.

Bonus : Pourquoi utiliser un wrapper pour IndexedDB

IndexedDB est une API de bas niveau qui nécessite une configuration importante avant d'être utilisée, ce qui peut être particulièrement pénible pour stocker des données de faible complexité. Contrairement à la plupart des API modernes basées sur des promesses, il est basé sur les événements. Les wrappers de promesse comme idb pour IndexedDB masquent certaines des fonctionnalités puissantes, mais surtout, masquent les machines complexes (telles que les transactions, la gestion des versions de schéma) fournies avec la bibliothèque IndexedDB.

Bonus : SQLite Wasm

Après l'abandon et la suppression de Web SQL de Chrome, Google a travaillé avec les responsables de la populaire base de données SQLite pour proposer un remplacement de Web SQL basé sur SQLite. Pour en savoir plus sur son utilisation, consultez SQLite Wasm dans le navigateur, compatible avec le système de fichiers privé Origin.

Conclusion

Fini le stockage limité et les invites à stocker de plus en plus de données. Les sites peuvent stocker efficacement toutes les ressources et données dont ils ont besoin pour s'exécuter. L'API StorageManager vous permet de déterminer la quantité dont vous disposez et celle que vous avez utilisée. De plus, avec le stockage persistant, vous pouvez le protéger de toute éviction, sauf si l'utilisateur le supprime.

Autres ressources

Merci

Remerciements particuliers à Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink et Victor Costan pour avoir relu ce guide. Merci à Eiji Kitamura, Addy Osmani et Marc Cohen, qui ont rédigé les articles originaux sur lesquels ils se basent. Eiji a écrit un outil utile appelé utilisation abusive du stockage du navigateur, qui a permis de valider le comportement actuel. Il vous permet de stocker autant de données que possible et de consulter les limites de stockage dans votre navigateur. Merci à François Beaufort qui a fouillé dans Safari pour déterminer ses limites de stockage et à Thomas Steiner pour avoir ajouté des informations sur le système de fichiers privé d'origine, les buckets de stockage, SQLite Wasm et une mise à jour globale du contenu en 2024.

L'image principale est de Guillaume Bolduc sur Unsplash.