Stockage pour le Web

Il existe de nombreuses options pour stocker des données dans le navigateur. Quelle solution répond le mieux à vos besoins ?

Les connexions Internet peuvent être instables ou inexistantes lors de vos déplacements. C'est pourquoi le fonctionnement hors connexion et des performances fiables sont des fonctionnalités courantes des 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 considérablement améliorer l'expérience utilisateur. Il existe plusieurs façons de mettre en cache les ressources d'application statiques (HTML, JavaScript, CSS, images, etc.) et vos données (données utilisateur, articles d'actualité, etc.). Mais quelle est la meilleure solution ? Combien pouvez-vous stocker ? Comment éviter qu'il ne soit évincé ?

Que dois-je utiliser ?

Voici une recommandation générale pour stocker des ressources:

IndexedDB et l'API Cache Storage sont compatibles avec tous les navigateurs récents. Ils sont à la fois asynchrones et ne bloquent pas le thread principal. Ils sont accessibles à partir de l'objet window, des workers Web et des service workers, ce qui vous permet de les utiliser facilement n'importe où dans le 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 peuvent entraîner des problèmes de performances importants.

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

Il convient d'éviter LocalStorage, car il est synchrone et bloque le thread principal. Elle est limitée à environ 5 Mo et ne peut contenir que des chaînes. LocalStorage n'est pas accessible depuis les services Web ou les service workers.

Les cookies ont une utilité spécifique, 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 petite quantité de données augmente considérablement la taille de chaque requête Web. Ils sont synchrones et ne sont pas accessibles depuis les workers Web. Comme LocalStorage et SessionStorage, les cookies sont limités aux chaînes.

L'API File System et l'API FileWriter fournissent des méthodes de lecture et d'écriture de 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.

L'API File System Access a été conçue pour permettre aux utilisateurs de lire et de modifier facilement 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. En outre, les autorisations ne sont pas conservées d'une session à l'autre.

WebSQL ne doit pas être utilisé, et l'utilisation existante doit être migrée vers IndexedDB. Il n'est plus pris en charge par presque tous les principaux navigateurs. Le W3C a cessé de gérer la spécification Web SQL en 2010, sans aucune mise à jour prévue.

Le cache d'application ne doit pas être utilisé, et l'utilisation existante doit être migrée vers les service workers et l'API Cache. Il est obsolète et ne sera bientôt plus pris en charge dans les navigateurs.

Combien puis-je stocker ?

En bref, beaucoup : au moins quelques centaines de mégaoctets, et potentiellement des centaines de gigaoctets ou plus. Les implémentations des navigateurs varient, mais l'espace de stockage disponible dépend généralement de celui 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 de site lorsque vous fermez toutes les fenêtres" dans Chrome, le quota de stockage est considérablement réduit à environ 300 Mo.
    • Pour en savoir plus sur l'implémentation de Chrome, consultez la demande d'autorisation n° 3896.
  • Internet Explorer 10 et versions ultérieures peuvent stocker jusqu'à 250 Mo et avertira l'utilisateur lorsque plus de 10 Mo ont été utilisés.
  • Firefox permet au navigateur d'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 déterminer l'espace encore disponible à l'aide de l'API StorageManager.
  • 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 trouvé aucune documentation officielle à ce sujet.
    • Si une PWA est ajoutée à l'écran d'accueil de la version mobile de Safari, elle semble créer un conteneur de stockage, et rien n'est partagé entre la PWA et la version mobile de Safari. Une fois le quota atteint pour une PWA installée, il ne semble plus être 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 demandait à l'utilisateur d'autoriser l'utilisation d'autres données. Par exemple, si l'origine utilise plus de 50 Mo, le navigateur invite l'utilisateur à l'autoriser à stocker jusqu'à 100 Mo, puis redemande à 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'à la limite du quota qui lui est alloué. Safari fait exception à la règle : lorsque le quota de stockage est dépassé, une invite s'affiche pour demander l'autorisation d'augmenter le quota alloué. Si une origine tente d'utiliser plus que le quota qui lui est alloué, les nouvelles tentatives d'écriture de données é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, ainsi que la quantité d'espace qu'elle utilise. Elle indique le nombre total d'octets utilisés par IndexedDB et l'API Cache, et permet de calculer approximativement l'espace de stockage restant 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.`);
}

StorageManager n'est pas encore implémenté dans tous les navigateurs. Vous devez donc le détecter avant de l'utiliser. Même lorsqu'il est disponible, vous devez toujours détecter 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 d'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 facilement toutes les données stockées.

Une nouvelle fonctionnalité a été ajoutée à Chrome 88 pour vous permettre 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é du disque. Accédez à Application > Stockage, cochez la case Simuler un quota de stockage personnalisé, puis saisissez un nombre valide pour simuler le quota de stockage.

Volet de stockage des outils de développement.

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

Comment gérer le dépassement du 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'une QuotaExceededError ou d'un autre problème. Ensuite, en fonction de la conception de votre application, décidez de la façon de gérer cela. Par exemple, supprimez le contenu qui n'a pas été consulté depuis longtemps, supprimez les données en fonction de la taille ou offrez aux utilisateurs un moyen de choisir ce qu'ils souhaitent supprimer.

IndexedDB et l'API Cache génèrent tous deux une erreur DOMError nommée QuotaExceededError lorsque vous dépassez 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 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 une erreur 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 l'éviction ?

Le stockage Web se divise en deux catégories : "Meilleur effort" et "Persistent". Dans la mesure du possible, l'espace de stockage peut être effacé par le navigateur sans interrompre l'utilisateur, mais la durabilité est moindre pour les données critiques ou à long terme. Le stockage persistant n'est pas automatiquement effacé lorsque l'espace de stockage 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 d'optimisation. Autrement dit, sauf si un site a demandé un stockage persistant, le navigateur peut évincer les données du site à sa seule discrétion, par exemple lorsque l'espace de stockage de l'appareil est faible.

La politique d'éviction de la meilleure approche est la suivante:

  • Les navigateurs basés sur Chromium commencent à évincer des données lorsque l'espace du navigateur est insuffisant. Toutes les données des sites de l'origine la moins récemment utilisée sont d'abord supprimées, puis la suivante, jusqu'à ce que le navigateur ne dépasse plus la limite.
  • Internet Explorer 10 ou version ultérieure n'évincera pas de données, mais empêchera l'écriture d'autres données.
  • Firefox commencera à évincer des données lorsque l'espace disque disponible sera saturé, en supprimant d'abord toutes les données de 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.
  • Auparavant, Safari n'expulsait pas les données, mais il a récemment implémenté une nouvelle limite de sept jours sur l'ensemble de l'espace de stockage accessible en écriture (voir ci-dessous).

À partir d'iOS et iPadOS 13.4 et Safari 13.1 sous macOS, une limite de sept jours est appliquée à l'ensemble du stockage de script accessible en écriture, y compris IndexedDB, l'enregistrement d'un service worker et l'API Cache. Cela signifie que Safari évince 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 d'éviction ne s'applique pas aux PWA installées qui ont été ajoutées à l'écran d'accueil. Pour en savoir plus, consultez l'article Full Third-Party Cookie Blocking and more (en anglais) sur le blog WebKit pour obtenir des informations complètes.

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 s'avérer particulièrement pénible pour le stockage de données simples. Contrairement à la plupart des API modernes basées sur des promesses, elles sont basées sur des événements. Les wrappers de promesse tels que idb pour IndexedDB masquent certaines des fonctionnalités puissantes, mais surtout, masquent le système complexe (par exemple, les transactions, la gestion des versions de schéma) fourni avec la bibliothèque IndexedDB.

Conclusion

Fini le temps de stockage limité, où il fallait inviter les utilisateurs à stocker de plus en plus de données. Les sites peuvent stocker de manière efficace toutes les ressources et données nécessaires à leur exécution. L'API StorageManager vous permet de déterminer les ressources dont vous disposez et celles que vous avez utilisées. De plus, grâce au stockage persistant, vous pouvez protéger l'appareil contre l'éviction, sauf si l'utilisateur le supprime.

Ressources supplémentaires

Merci

Un grand merci à Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink et Victor Costan pour avoir relu cet article. Merci à Eiji Kitamura, Addy Osmani et Marc Cohen qui ont rédigé les articles originaux sur lesquels cela repose. Eiji a écrit un outil utile appelé Browser Storage Abuser qui s'est avéré utile pour valider le comportement actuel. Elle vous permet de stocker autant de données que possible et de voir les limites de stockage dans votre navigateur. Un grand merci à François Beaufort, qui s'est penché sur Safari pour déterminer ses limites de stockage.

L'image héros a été réalisée par Guillaume Bolduc sur Unsplash.