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 après chaque opération de stockage.
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 du code semblable à ce qui suit:
// 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 valeurusage
globale.quota
reflète la quantité d'espace actuellement réservée pour 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é. 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});
}