הערכת נפח האחסון הזמין

tl;dr

ב-Chrome 61, עם עוד דפדפנים למעקב, מוצגת עכשיו הערכה של נפח האחסון שאפליקציית אינטרנט משתמשת בו וכמות האחסון הזמינה דרך:

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

אפליקציות אינטרנט מודרניות ואחסון נתונים

כשחושבים על צורכי האחסון של אפליקציית אינטרנט מודרנית, מומלץ לחלק מה המאוחסן לשתי קטגוריות: הנתונים העיקריים שדרושים לטעינה את אפליקציית האינטרנט, ואת הנתונים הדרושים לאינטראקציה משמעותית עם המשתמשים פעם אחת שהאפליקציה נטענת.

סוג הנתונים הראשון, שנחוץ כדי לטעון את אפליקציית האינטרנט, הוא HTML, JavaScript, CSS ואולי גם כמה תמונות. Service Worker וגם את Cache Storage API, לספק את התשתית הנדרשת לשמירת משאבי הליבה האלה, ואז להשתמש מאוחר יותר כדי לטעון במהירות את אפליקציית האינטרנט, ובאופן אידיאלי, לעקוף את הרשת לגמרי. (כלים שמשתלבים בתהליך ה-build של אפליקציית האינטרנט, כמו ספריות Workbox או ישנות יותר sw-precache, יכולה להיות אוטומטית לחלוטין את תהליך האחסון, העדכון והשימוש בסוג הזה data.)

אבל מה לגבי סוג הנתונים השני? אלה משאבים שלא נדרשים כדי טוען את יישום האינטרנט, אבל עשוי להיות לו תפקיד קריטי עבור המשתמשים באופן כללי חוויה אישית. למשל, אם אתם כותבים אפליקציית אינטרנט לעריכת תמונות, אפשר שרוצים לשמור עותק מקומי אחד או יותר של תמונה, כדי שהמשתמשים יוכלו בין שכתובים וביטול העבודה שלהם. אם אתם מפתחים מדיה אופליין כדי לייעל את חוויית ההפעלה, חשוב לשמור קובצי אודיו או וידאו באופן מקומי . כל אפליקציית אינטרנט שאפשר להתאים אישית תצטרך לשמור חלק סוג של מידע על מצב. איך יודעים כמה מקום פנוי באחסון זמן ריצה מהסוג הזה, ומה קורה כשנגמר המקום?

הנתונים הקודמים: window.webkitStorageInfo ו-navigator.webkitTemporaryStorage

בעבר דפדפנים תמכו בסוג זה של בחינה עצמית באמצעות כמו הממשקים הישנים, שיצאו משימוש window.webkitStorageInfo והלא ממש סטנדרטי, navigator.webkitTemporaryStorage הממשקים האלה סיפקו מידע שימושי, אבל אין להם בעתיד כסטנדרטים של אינטרנט.

כאן נכנסים לתקן האחסון whatWG נכנס לתמונה.

העתיד: navigator.storage

במסגרת העבודה המתמשכת על Storage Living Standard, פיתחנו כמה ממשקי API שימושיים אותו StorageManager בממשק הזה, שחשוף לדפדפנים navigator.storage. בדומה להרבה ממשקי API חדשים אחרים באינטרנט, navigator.storage זמין רק באתרים מאובטחים (משמשים באמצעות מקורות HTTPS או Localhost).

בשנה שעברה הצגנו את navigator.storage.persist() שמאפשרת לאפליקציית האינטרנט לבקש נפח אחסון פטור מניקוי אוטומטי.

הוא מחובר עכשיו באמצעות השיטה navigator.storage.estimate(), שמשמשת תחליף מודרני ל-navigator.webkitTemporaryStorage.queryUsageAndQuota(). הפונקציה estimate() מחזירה מידע דומה, אבל היא חושפת מבוסס-הבטחה, שעומדת בדרישות של ממשקי API אסינכרוניים אחרים ומודרניים. ההבטחה החזרות estimate() מקודדות עם אובייקט שמכיל שני מאפיינים: usage, שמייצג את מספר הבייטים בשימוש כרגע, ו-quota, שמייצג את את מספר הבייטים המקסימלי שאפשר לאחסן באמצעות origin. (כמו כל דבר אחר שקשור לאחסון, המכסה חלה על כל origin.)

אם אפליקציית אינטרנט מנסה לאחסן - באמצעות, לדוגמה, IndexedDB או Cache Storage API – נתונים גדולים מספיק כדי להעביר מקור נתון מכסה זמינה, הבקשה תיכשל עם QuotaExceededError חריג.

אומדני נפח האחסון בפעולה

אופן השימוש המדויק ב-estimate() תלוי בסוג הנתונים שהאפליקציה צריכה כדי החנות. לדוגמה, אפשר לעדכן פקד בממשק שמאפשר למשתמשים לדעת כמה מקום נמצא בשימוש בסיום כל פעולת אחסון. לאחר מכן מומלץ לספק ממשק שמאפשר למשתמשים לנקות נתונים באופן ידני שכבר לא צריך. אפשר לכתוב קוד בשורות של:

// 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;
  }
}

עד כמה מדויקת ההערכה?

קשה לפספס את העובדה שהנתונים שמתקבלים מהפונקציה הערכה של השטח שבו מקור מסוים משתמש. הוא נמצא שם בפונקציה שם! הערכים usage וגם לא quota לא מיועדים להיות יציבים, לכן מומלץ להביא בחשבון את הנקודות הבאות:

  • usage משקף את כמות הבייטים שמשתמשי מקור נתון בפועל אותו מקור נתונים, שבתורו יכולים להיות מושפעים מטכניקות דחיסה פנימיות, בלוקים של הקצאה בגודל קבוע שעשויים לכלול שטח שלא נוצל ואת הנוכחות מתוך "מצבה" רשומות שעלולות להיווצר באופן זמני אחרי מחיקה. כדי למנוע דליפה של פרטי גודל מדויק, חוצה-מקורות, משאבים אטומים שנשמר באופן מקומי, עשוי לתרום למרווח פנימי נוספים ב-usage הכולל עם ערך מסוים.
  • quota משקפת את נפח האחסון ששמור כרגע למקור כלשהו. תלוי בגורמים קבועים מסוימים כמו גודל האחסון הכולל, אבל גם מספר הגורמים שעשויים להיות תנודתיים, כולל נפח האחסון שלא בשימוש כרגע. כך שכמו שאפליקציות אחרות במכשיר כותבים או מוחקים כמות הנתונים, כלומר כמות השטח שהדפדפן מוכן להקדיש סביר להניח שמקור האפליקציה ישתנה.

ההווה: זיהוי תכונות וחלופות

estimate() מופעל כברירת מחדל החל מ-Chrome 61. Firefox הוא קיים ניסוי עם navigator.storage, אבל החל מאוגוסט 2017, הוא לא הופעל מופעל כברירת מחדל. צריך: הפעלת ההעדפה dom.storageManager.enabled כדי לבדוק אותו.

כשעובדים עם פונקציונליות שעדיין לא נתמכת בכל הדפדפנים, הוא חובה. אפשר לשלב בין זיהוי תכונות wrapper מבוסס-הבטחה מעל navigator.webkitTemporaryStorage הישן שיטות שמספקות ממשק עקבי שמבוסס על:

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});
}