Estimer l'espace de stockage disponible

Résumé

Chrome 61, avec d'autres navigateurs à suivre, affiche désormais une estimation de l'espace de stockage utilisé par une application Web et de l'espace disponible via:

if ('storage' in navigator && 'estimate' in navigator.storage) {
  navigator.storage.estimate().then(({usage, quota}) => {
    console.log(`Using ${usage} out of ${quota} bytes.`);
  });
}

Applications Web modernes et stockage de données

Lorsque vous réfléchissez aux besoins de stockage d'une application Web moderne, il est utile de diviser ce qui est stocké en deux catégories: les données de base nécessaires pour charger l'application Web et les données nécessaires pour une interaction utilisateur significative une fois l'application chargée.

Le premier type de données, qui est nécessaire pour charger votre application Web, se compose de code HTML, JavaScript, CSS et peut-être d'images. Les service workers, ainsi que l'API Cache Storage, fournissent l'infrastructure nécessaire pour enregistrer ces ressources de base, puis les utiliser ultérieurement pour charger rapidement votre application Web, en contournant idéalement entièrement le réseau. (Les outils qui s'intègrent au processus de compilation de votre application Web, comme les nouvelles bibliothèques Workbox ou l'ancienne sw-precache, peuvent entièrement automatiser le processus de stockage, de mise à jour et d'utilisation de ce type de données.)

Mais qu'en est-il de l'autre type de données ? Il s'agit de ressources qui ne sont pas nécessaires pour charger votre application Web, mais qui peuvent jouer un rôle crucial dans l'expérience utilisateur globale. Si vous écrivez une application Web de retouche d'image, par exemple, vous pouvez enregistrer une ou plusieurs copies locales d'une image, ce qui permet aux utilisateurs de basculer entre les révisions et d'annuler leur travail. Si vous développez une expérience de lecture multimédia hors connexion, l'enregistrement local de fichiers audio ou vidéo est une fonctionnalité essentielle. Chaque application Web personnalisable doit finir par enregistrer une sorte d'informations d'état. Comment savoir combien d'espace est disponible pour ce type de stockage d'exécution et que se passe-t-il lorsque l'espace est insuffisant ?

Anciens: window.webkitStorageInfo et navigator.webkitTemporaryStorage

Les navigateurs ont toujours accepté ce type d'introspection via des interfaces préfixées, comme l'ancienne (et obsolète) window.webkitStorageInfo et la moins ancienne, mais toujours non standard, navigator.webkitTemporaryStorage. Bien que ces interfaces fournissent des informations utiles, elles n'ont pas d'avenir en tant que normes Web.

C'est là que la norme de stockage WHATWG entre en jeu.

L'avenir: navigator.storage

Dans le cadre des travaux en cours sur le Storage Living Standard, quelques API utiles ont été ajoutées à l'interface StorageManager, qui est exposée aux navigateurs en tant que navigator.storage. Comme de nombreuses autres API Web plus récentes, navigator.storage n'est disponible que sur des origines sécurisées (servées via HTTPS ou localhost).

L'année dernière, nous avons introduit la méthode navigator.storage.persist(), qui permet à votre application Web de demander que son espace de stockage soit exempté du nettoyage automatique.

Elle est désormais associée à la méthode navigator.storage.estimate(), qui sert de remplacement moderne à navigator.webkitTemporaryStorage.queryUsageAndQuota(). estimate() renvoie des informations similaires, mais expose une interface basée sur des promesses, qui est conforme aux autres API asynchrones modernes. La promesse renvoyée par estimate() se résout avec un objet contenant deux propriétés: usage, qui représente le nombre d'octets actuellement utilisés, et quota, qui représente le nombre maximal d'octets pouvant être stockés par l'origine actuelle. (Comme tout ce qui concerne le stockage, le quota s'applique à l'ensemble de l'origine.)

Si une application Web tente de stocker des données suffisamment volumineuses pour dépasser le quota disponible d'une origine donnée à l'aide, par exemple, d'IndexedDB ou de l'API Cache Storage, la requête échoue avec une exception QuotaExceededError.

Estimations de l'espace de stockage en action

La manière exacte dont vous utilisez estimate() dépend du type de données que votre application doit stocker. Par exemple, vous pouvez mettre à jour une commande de votre interface pour indiquer aux utilisateurs la quantité d'espace utilisée une fois chaque opération de stockage terminée. Idéalement, vous devez fournir une interface permettant aux utilisateurs de nettoyer manuellement les données qui ne sont plus nécessaires. Vous pouvez écrire le code suivant:

// For a primer on async/await, see
// https://developers.google.com/web/fundamentals/getting-started/primers/async-functions
async function storeDataAndUpdateUI(dataUrl) {
  // Pro-tip: The Cache Storage API is available outside of service workers!
  // See https://googlechrome.github.io/samples/service-worker/window-caches/
  const cache = await caches.open('data-cache');
  await cache.add(dataUrl);

  if ('storage' in navigator && 'estimate' in navigator.storage) {
    const {usage, quota} = await navigator.storage.estimate();
    const percentUsed = Math.round(usage / quota * 100);
    const usageInMib = Math.round(usage / (1024 * 1024));
    const quotaInMib = Math.round(quota / (1024 * 1024));

    const details = `${usageInMib} out of ${quotaInMib} MiB used (${percentUsed}%)`;

    // This assumes there's a <span id="storageEstimate"> or similar on the page.
    document.querySelector('#storageEstimate').innerText = details;
  }
}

Quelle est la précision de l'estimation ?

Il est difficile de ne pas remarquer que les données renvoyées par la fonction ne sont qu'une estimation de l'espace utilisé par une origine. C'est indiqué dans le nom de la fonction. Les valeurs usage et quota ne sont pas destinées à être stables. Nous vous recommandons donc de prendre en compte les points suivants:

  • usage reflète le nombre d'octets qu'une origine donnée utilise effectivement pour les données de même origine, qui peuvent à leur tour être affectées par des techniques de compression internes, des blocs d'allocation de taille fixe pouvant inclure de l'espace inutilisé et la présence d'enregistrements de "tombstone" pouvant être créés temporairement après une suppression. Pour éviter la fuite d'informations de taille exacte, les ressources opaques multi-origines enregistrées localement peuvent contribuer à ajouter des octets de remplissage supplémentaires à la valeur usage globale.
  • quota reflète la quantité d'espace actuellement réservée à une origine. La valeur dépend de certains facteurs constants, comme la taille globale de l'espace de stockage, mais également d'un certain nombre de facteurs potentiellement volatils, y compris la quantité d'espace de stockage actuellement inutilisée. Ainsi, à mesure que d'autres applications d'un appareil écrivent ou suppriment des données, la quantité d'espace que le navigateur est prêt à consacrer à l'origine de votre application Web est susceptible de changer.

Aujourd'hui: détection des fonctionnalités et solutions de remplacement

estimate() est activé par défaut à partir de Chrome 61. Firefox teste navigator.storage, mais depuis août 2017, il n'est pas activé par défaut. Vous devez activer la préférence dom.storageManager.enabled pour la tester.

Lorsque vous utilisez des fonctionnalités qui ne sont pas encore compatibles avec tous les navigateurs, la détection des fonctionnalités est indispensable. Vous pouvez combiner la détection de fonctionnalités avec un wrapper basé sur des promesses au-dessus des anciennes méthodes navigator.webkitTemporaryStorage pour fournir une interface cohérente, par exemple:

function storageEstimateWrapper() {
  if ('storage' in navigator && 'estimate' in navigator.storage) {
    // We've got the real thing! Return its response.
    return navigator.storage.estimate();
  }

  if ('webkitTemporaryStorage' in navigator &&
      'queryUsageAndQuota' in navigator.webkitTemporaryStorage) {
    // Return a promise-based wrapper that will follow the expected interface.
    return new Promise(function(resolve, reject) {
      navigator.webkitTemporaryStorage.queryUsageAndQuota(
        function(usage, quota) {resolve({usage: usage, quota: quota})},
        reject
      );
    });
  }

  // If we can't estimate the values, return a Promise that resolves with NaN.
  return Promise.resolve({usage: NaN, quota: NaN});
}