Stockage pour le Web

Il existe de nombreuses options différentes pour stocker des données dans le navigateur. Quelle est la meilleure pour vos besoins ?

Les connexions Internet peuvent être instables ou inexistantes en déplacement. C'est pourquoi la compatibilité hors connexion et les performances fiables sont des fonctionnalités courantes dans les applications Web progressives. Même dans des environnements sans fil parfaits, une utilisation judicieuse de la mise en cache et d'autres techniques de stockage peut améliorer considérablement l'expérience utilisateur. Il existe plusieurs façons de mettre en cache les ressources statiques de votre application (HTML, JavaScript, CSS, images, etc.) et les données (données utilisateur, articles d'actualité, etc.). Mais quelle est la meilleure solution ? Quelle quantité pouvez-vous stocker ? Comment éviter qu'elle ne soit supprimée ?

Que dois-je utiliser ?

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

IndexedDB, l'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 qui n'est disponible que dans les Web Workers). Ils sont accessibles à partir de l'objet window, des Web Workers et des service workers, ce qui 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 leur utilisation est limitée et ils peuvent entraîner des problèmes de performances importants.

SessionStorage est spécifique à un onglet et limité à la durée de vie de l'onglet. Il 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. Comme il est spécifique à un onglet, il n'est pas accessible à partir des Web Workers ni des service workers.

LocalStorage doit être évité, 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 à partir des Web Workers ni des service workers.

Les cookies ont leur utilité, mais 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é de données supérieure à une petite quantité augmentera considérablement la taille de chaque requête Web. Ils sont synchrones et ne sont pas accessibles à partir des Web Workers. Comme LocalStorage et SessionStorage, les cookies sont limités 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 l'autorisation avant qu'une page puisse lire ou écrire dans un fichier local. Les autorisations ne sont pas conservées d'une session à l'autre, sauf si un descripteur de fichier est mis en cache dans IndexedDB. L'API File System Access est plus adaptée aux cas d'utilisation tels que les éditeurs, où vous devez ouvrir un fichier, le modifier, puis éventuellement enregistrer les modifications dans le fichier.

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

Quelle quantité 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é de stockage disponible est généralement basée sur la quantité de stockage disponible sur l'appareil.

  • Chrome permet au navigateur d'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. D'autres navigateurs basés sur Chromium peuvent être 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 à la fermeture de toutes les fenêtres" dans Chrome, le quota de stockage est considérablement réduit à un maximum d'environ 300 Mo.
  • Firefox permet au navigateur d'utiliser jusqu'à 50% de l'espace disque libre. Un groupe eTLD+1 (par exemple, example.com, www.example.com et foo.bar.example.com) peut utiliser jusqu'à 2 Go. Vous pouvez utiliser l' API StorageManager pour déterminer l'espace encore disponible.
  • Safari (sur ordinateur et sur mobile) semble autoriser environ 1 Go. Lorsque la limite est atteinte, Safari invite l'utilisateur à augmenter la limite par incréments de 200 Mo. Je n'ai pas trouvé de documentation officielle à ce sujet.
    • Si une PWA est ajoutée à l'écran d'accueil sur Safari mobile, elle crée un conteneur de stockage et rien n'est partagé entre la PWA et Safari mobile. Une fois le quota atteint pour une PWA installée, il ne semble pas y avoir de moyen 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 invitait l'utilisateur à autoriser le stockage de 100 Mo, puis lui demandait à nouveau par incréments de 50 Mo.

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

Comment vérifier l'espace de stockage disponible ?

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

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 intercepter les erreurs de dépassement de quota (voir ci-dessous). Dans certains cas, il est possible que le quota disponible dépasse la quantité réelle 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. Elle vous permet de remplacer le quota de stockage du site dans le volet "Storage" (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 à Storage (Stockage), cochez la case Simulate custom storage quota (Simuler un quota de stockage personnalisé) et saisissez n'importe quel nombre valide pour simuler le quota de stockage.

Lors de la création de ce guide, j'ai écrit un outil simple pour tenter d'utiliser rapidement le plus d'espace de stockage possible. Il s'agit d'un moyen rapide d'expérimenter différents mécanismes de stockage et de voir ce qui se passe lorsque vous utilisez tout votre quota.

Comment gérer le dépassement du quota ?

Que faire lorsque vous dépassez le quota ? Plus important encore, vous devez toujours intercepter et gérer les erreurs d'écriture, qu'il s'agisse d'une QuotaExceededError ou d'autre chose. Ensuite, en fonction de la conception de votre application, décidez comment la gérer. Par exemple, supprimez le contenu auquel vous n'avez pas 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 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 sera appelé, en transmettant un événement. L'événement inclura une DOMException dans la propriété d'erreur. La vérification du name de l'erreur renverra 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 une QuotaExceededError DOMException.

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 la suppression ?

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

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

La règle de suppression pour le meilleur effort est la suivante :

  • Les navigateurs basés sur Chromium commenceront à supprimer les données des sites lorsque le navigateur manque d'espace, en effaçant d'abord toutes les données des sites de l'origine la moins récemment utilisée, puis la suivante, jusqu'à ce que le navigateur ne dépasse plus la limite.
  • Firefox commencera à supprimer les données lorsque l'espace disque disponible sera plein, en effaçant d'abord toutes les données des sites de l'origine la moins récemment utilisée, puis la suivante, jusqu'à ce que le navigateur ne dépasse plus la limite.
  • Auparavant, Safari ne supprimait pas les données, mais il a récemment mis en place une nouvelle limite de sept jours pour tous les espaces de stockage accessibles en écriture (voir ci-dessous).

À partir d'iOS et d'iPadOS 13.4 et de Safari 13.1 sur macOS, une limite de sept jours s'applique à tous les espaces de stockage accessibles en écriture par script, y compris IndexedDB, l'enregistrement des service workers et l'API Cache. Cela signifie que Safari supprimera tout le contenu du cache après sept jours d'utilisation de Safari si l'utilisateur n'interagit pas avec le site. Cette règle de suppression ne s'applique pas aux PWA installées qui ont été ajoutées à l'écran d'accueil. Pour en savoir plus, consultez la section Full Third-Party Cookie Blocking and More (Blocage complet des cookies tiers et plus) sur le blog WebKit.

Buckets de stockage

L'idée principale de l'API Storage Buckets est d'accorder aux sites la possibilité de créer plusieurs buckets de stockage, où le navigateur peut choisir de supprimer chaque bucket indépendamment des autres. Cela permet aux développeurs de spécifier la priorité de suppression pour s'assurer que les données les plus importantes 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 utilisation, ce qui peut être particulièrement pénible pour stocker des données peu complexes. Contrairement à la plupart des API modernes basées sur des promesses, elle est basée sur des événements. Les wrappers de promesses tels que idb pour IndexedDB masquent certaines des fonctionnalités puissantes, mais surtout, ils masquent le mécanisme complexe (par exemple, les transactions, la gestion des versions du schéma) fourni avec la bibliothèque IndexedDB.

Bonus: SQLite Wasm

Après l'abandon et la suppression de Web SQL de Chrome, Google a collaboré avec les responsables de la base de données SQLite populaire pour proposer un remplacement de Web SQL basé sur SQLite. Pour savoir comment l'utiliser, consultez la section SQLite Wasm in the browser backed by the Origin Private File System (SQLite Wasm dans le navigateur avec le système de fichiers privé d'origine).

Conclusion

L'époque du stockage limité et de l'invitation de l'utilisateur à stocker de plus en plus de données est révolue. Les sites peuvent stocker efficacement toutes les ressources et données dont ils ont besoin pour s'exécuter. À l'aide de l'API StorageManager, vous pouvez déterminer la quantité disponible et la quantité que vous avez utilisée. Avec le stockage persistant, vous pouvez le protéger contre la suppression, sauf si l'utilisateur le supprime.

Ressources supplémentaires

Merci

Merci tout particulièrement à Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink et Victor Costan pour avoir examiné ce guide. Merci à Eiji Kitamura, Addy Osmani et Marc Cohen qui ont écrit les articles originaux sur lesquels ce guide est basé. Eiji a créé un outil utile appelé Browser Storage Abuser, qui a permis de valider le comportement actuel. Il vous permet de stocker autant de données que possible et de voir les limites de stockage de votre navigateur. Merci à François Beaufort, qui a exploré Safari pour déterminer ses limites de stockage, et à Thomas Steiner, qui a 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.