ข้ามไปยังเนื้อหา

Quota และ persistent storage

browser ไม่ได้ให้ storage ไม่จำกัดแก่ทุก origin แต่จะจัดสรร quota ที่เป็นจำนวนสูงสุดที่ origin อาจใช้ โดยพิจารณาจาก disk ที่มีอยู่และนโยบาย browser quota สำหรับ IndexedDB, Cache API และ OPFS มาจาก shared pool ที่เรียกว่า “default” storage bucket

navigator.storage.estimate() API คืนค่า snapshot ของปริมาณ quota ที่ origin ของคุณใช้ไปแล้ว

const { quota, usage } = await navigator.storage.estimate();
console.log('Quota :', (quota / 1024 / 1024).toFixed(1), 'MB');
console.log('Usage :', (usage / 1024 / 1024).toFixed(2), 'MB');

ค่า usage ที่คืนมาครอบคลุม IndexedDB, Cache API และ OPFS รวมกัน ไม่รวม localStorage หรือ cookies ซึ่งมี limits แยกต่างหากที่เล็กกว่า

โดยค่าเริ่มต้น ทุก origin จะได้รับ best-effort storage ภายใต้ disk ที่มีน้อย browser มีอิสระที่จะ evict ข้อมูล best-effort ได้ โดยไม่มีการแจ้งเตือน เพื่อเพิ่มพื้นที่ ลำดับการ evict มักเป็น origin ที่ใช้งานล่าสุดน้อยที่สุดก่อน

คุณสามารถขอให้ browser อัปเกรด origin ของคุณเป็น persistent storage ข้อมูล persistent จะไม่ถูก evict โดยไม่มีการกระทำที่ชัดเจนจากผู้ใช้ (การล้าง site data) ในการขอ:

const granted = await navigator.storage.persist();
console.log('Persistent storage granted:', granted); // true หรือ false

ในการตรวจสอบสถานะปัจจุบันโดยไม่ขอ:

const isPersisted = await navigator.storage.persisted();
console.log('Already persistent:', isPersisted);

browser ใช้ engagement heuristics ในการตัดสินใจว่าจะอนุมัติ request แบบเงียบ ๆ หรือแสดง permission prompt ปัจจัยที่เพิ่มโอกาสในการได้รับการอนุมัติโดยอัตโนมัติ ได้แก่:

  • Site ถูก install เป็น PWA (เพิ่มใน Home Screen / ติดตั้งผ่าน browser UI)
  • ผู้ใช้เยี่ยมชม site บ่อยครั้ง
  • ผู้ใช้ bookmark site หรือมอบ permissions อื่น ๆ (push notifications, geolocation)
  • บาง browser (Firefox) จะถามผู้ใช้เสมอ browser ที่ใช้ Chromium อาจอนุมัติเงียบ ๆ
flowchart TD
  A[Origin stores data] --> B{Durability tier}
  B -->|Default| C[Best-effort storage]
  B -->|After persist granted| D[Persistent storage]
  C --> E[Browser MAY evict under\ndisk pressure]
  E --> F[Data silently deleted]
  D --> G[Browser will NEVER evict\nwithout user action]
  G --> H[Data safe until user\nclears site data]
Best-effort storage อาจถูก evict เงียบ ๆ; persistent storage ต้องการการกระทำที่ชัดเจนจากผู้ใช้เพื่อลบ

snippet ด้านล่างเรียกทั้ง estimate() และ persisted() และบันทึกผลลัพธ์ ไม่มีการเขียน demo keys ดังนั้นไม่ต้องทำความสะอาด

Browser Storage
ตัวเลือกBenefitCost
Best-effort storage (default)ไม่ต้องขอ permission จากผู้ใช้ ใช้งานได้ทันทีbrowser อาจ evict ข้อมูลเงียบ ๆ เมื่อ disk เหลือน้อย
Persistent storage (persist())ข้อมูลปลอดภัยจนกว่าผู้ใช้จะล้าง site data เองต้องพึ่ง engagement heuristics ของ browser ซึ่งควบคุมไม่ได้ทั้งหมด และบาง browser จะแสดง permission prompt ที่ผู้ใช้อาจปฏิเสธ
เรียก estimate() ก่อนเขียนข้อมูลก้อนใหญ่รู้ headroom ที่เหลือ ป้องกัน QuotaExceededError กลางทางเพิ่ม async round-trip หนึ่งครั้งก่อนเริ่ม operation จริง
  • คิดว่า IndexedDB เท่ากับ “เก็บถาวรตลอดไป” — โดย default ทุก origin ได้แค่ best-effort storage เท่านั้น ต้องเรียก navigator.storage.persist() เพื่อขออัปเกรดเป็น persistent จริง ๆ
  • ไม่เช็ก quota ก่อนเขียนข้อมูลก้อนใหญ่ — เขียนไปเรื่อย ๆ โดยไม่เรียก navigator.storage.estimate() ก่อน ทำให้เจอ QuotaExceededError แบบไม่ทันตั้งตัวเมื่อ disk เหลือน้อย
  • สับสนระหว่าง usage ที่ได้จาก estimate() กับ localStorage — ค่าที่คืนมาครอบคลุมแค่ IndexedDB, Cache API และ OPFS เท่านั้น ไม่รวม localStorage หรือ cookies ซึ่งมี limit แยกต่างหาก

💡 ตัวอย่างจากของจริง

Google Photos — เรียก navigator.storage.persist() หลังผู้ใช้ install เป็น PWA เพื่อให้ cached thumbnails และ offline queue ไม่ถูก evict ระหว่างใช้งานแบบ offline

Notion / Google Docs offline mode — เช็ก navigator.storage.estimate() ก่อน sync document ขนาดใหญ่ลง IndexedDB เพื่อเตือนผู้ใช้ล่วงหน้าเมื่อ quota ใกล้เต็ม แทนที่จะปล่อยให้ sync ล้มเหลวกลางทาง

navigator.storage.estimate() return อะไร?
ภายใต้เงื่อนไขใดที่ browser สามารถลบข้อมูล best-effort storage เงียบ ๆ?
navigator.storage.persist() คืน Promise ค่าที่ resolve หมายความว่าอะไร?