فضای ذخیره‌سازی برای وب

گزینه‌های مختلفی برای ذخیره کردن داده‌ها در مرورگر وجود دارد. کدام‌یک برای نیازهای شما بهتر است؟

اتصال اینترنت درحال حرکت ممکن است ضعیف باشد یا اصلاً وجود نداشته باشد، به همین دلیل است که پشتیبانی آفلاین و عملکرد قابل‌اطمینان از ویژگی‌های رایج در برنامه‌های وب پیش‌رونده است. حتی در محیط‌های بی‌سیم عالی، استفاده عاقلانه از حافظه پنهان و سایر تکنیک‌های ذخیره‌سازی می‌تواند تجربه کاربری را به‌طور قابل‌توجهی بهبود بخشد. چندین روش برای ذخیره کردن منابع برنامه ایستا (HTML،‏ JavaScript،‏ CSS، تصاویر، و غیره) و داده‌ها (داده‌های کاربر، مقاله‌های خبری، و غیره) در حافظه نهان وجود دارد. اما کدام راه‌حل بهترین است؟ چقدر می‌توانید ذخیره کنید؟ چگونه از تخلیه آن جلوگیری می‌کنید؟

از چه چیزی باید استفاده کنم؟

در اینجا توصیه‌ای کلی برای ذخیره کردن منابع ارائه شده است:

‫IndexedDB،‏ OPFS، و Cache Storage API در همه مرورگرهای نو پشتیبانی می‌شوند. این APIها ناهمگام هستند و رشته اصلی را مسدود نمی‌کنند (اما یک نوع همگام از OPFS نیز وجود دارد که منحصراً در وب‌ورکرها دردسترس است). این APIها ازطریق شیء window، وب‌ورکرها، و سرویس‌ورکرها قابل‌دسترسی هستند، که استفاده از آن‌ها را در هرجایی از کد شما ممکن می‌سازد.

سایر سازوکارهای ذخیره‌سازی چطور؟

چندین سازوکار ذخیره‌سازی دیگر در مرورگر دردسترس است، اما استفاده محدودی دارند و ممکن است مشکلات عملکردی قابل‌توجهی ایجاد کنند.

SessionStorage مختص برگه است و به طول عمر برگه محدود می‌شود. این روش ممکن است برای ذخیره کردن مقادیر کمی از اطلاعات خاص جلسه، برای مثال کلید IndexedDB، مفید باشد. باید بااحتیاط از آن استفاده شود چون هم‌زمان است و رشته اصلی را مسدود خواهد کرد. این حافظه به حدود ۵ مگابایت محدود است و فقط می‌تواند حاوی رشته باشد. چون مختص برگه است، ازطریق وب‌ورکرها یا سرویس‌ورکرها قابل‌دسترسی نیست.

باید از LocalStorage اجتناب شود زیرا همگام است و رشته اصلی را مسدود می‌کند. این حافظه به حدود ۵ مگابایت محدود است و فقط می‌تواند رشته‌ها را دربرگیرد. LocalStorage ازطریق وب‌ورکرها یا سرویس‌ورکرها دردسترس نیست.

کوکی‌ها کاربردهای خودشان را دارند، اما نباید برای ذخیره‌سازی استفاده شوند. کوکی‌ها با هر درخواست HTTP ارسال می‌شوند، بنابراین ذخیره کردن هر چیزی بیشتر از مقدار کمی داده، اندازه هر درخواست وب را به‌طور قابل‌توجهی افزایش می‌دهد. این APIها همگام هستند و ازطریق وب‌ورکرها قابل‌دسترسی نیستند. کوکی‌ها، مانند LocalStorage و SessionStorage، فقط به رشته‌ها محدود می‌شوند.

File System Access API به‌گونه‌ای طراحی شده است که کاربران بتوانند فایل‌های موجود در سیستم فایل محلی‌شان را بخوانند و ویرایش کنند. کاربر باید قبل‌از اینکه صفحه‌ای بتواند در فایل محلی بخواند یا بنویسد، اجازه اعطا کند و اجازه‌ها در جلسه‌ها حفظ نمی‌شوند، مگراینکه دسته فایل در IndexedDB ذخیره شده باشد. «میانای برنامه‌سازی کاربردی دسترسی سیستم فایل» برای موارد استفاده‌ای مثل ویرایشگرها مناسب‌تر است، که در آن‌ها باید فایلی را باز کنید، آن را اصلاح کنید، و سپس احتمالاً تغییرات را در فایل ذخیره کنید.

File System API و FileWriter API روش‌هایی برای خواندن و نوشتن فایل‌ها در سیستم فایل جعبه امن ارائه می‌دهند. اگرچه این روش ناهمگام است، توصیه نمی‌شود زیرا فقط در مرورگرهای مبتنی بر Chromium دردسترس است.

چقدر می‌توانم ذخیره کنم؟

به‌طور خلاصه، خیلی زیاد، حداقل چند صد مگابایت، و احتمالاً صدها گیگابایت یا بیشتر. پیاده‌سازی‌های مرورگر متفاوت است، اما مقدار فضای ذخیره‌سازی دردسترس معمولاً براساس مقدار فضای ذخیره‌سازی دردسترس در دستگاه است.

  • Chrome به مرورگر اجازه می‌دهد تا ۸۰٪ از کل فضای دیسک را استفاده کند. یک مبدأ می‌تواند تا ۶۰٪ از کل فضای دیسک را استفاده کند. می‌توانید از StorageManager API برای تعیین حداکثر سهمیه دردسترس استفاده کنید. مرورگرهای دیگر مبتنی بر Chromium ممکن است متفاوت باشند.
    • در حالت ناشناس، Chrome مقدار فضای ذخیره‌سازی‌ای را که یک مبدأ می‌تواند استفاده کند به حدود ۵٪ از کل فضای دیسک کاهش می‌دهد.
    • اگر کاربر «پاک کردن کوکی‌ها و داده‌های سایت هنگام بستن همه پنجره‌ها» را در Chrome فعال کرده باشد، سهمیه فضای ذخیره‌سازی به‌طور قابل‌توجهی به حداکثر حدود ۳۰۰ مگابایت کاهش می‌یابد.
  • Firefox به مرورگر اجازه می‌دهد تا ۵۰٪ از فضای دیسک آزاد را استفاده کند. گروه eTLD+1 (برای مثال، example.com،‏ www.example.com، و foo.bar.example.com) می‌تواند تا ۲ گیگابایت استفاده کند. می‌توانید از StorageManager API استفاده کنید تا ببینید چقدر فضا هنوز دردسترس است.
  • به‌نظر می‌رسد Safari (هم در رایانه و هم در تلفن همراه) حدود ۱ گیگابایت را مجاز می‌کند. وقتی به حد مجاز رسید، Safari از کاربر درخواست می‌کند که حد مجاز را با افزایش‌های ۲۰۰ مگابایتی افزایش دهد. نتوانستم هیچ سند رسمی‌ای درباره این موضوع پیدا کنم.
    • اگر PWA به صفحه اصلی در Safari تلفن همراه اضافه شود، محفظه ذخیره‌سازی جدیدی ایجاد می‌کند و هیچ‌چیز بین PWA و Safari تلفن همراه هم‌رسانی نمی‌شود. وقتی که سهمیه برای «برنامه وب پیش‌رو» نصب‌شده به‌پایان می‌رسد، به‌نظر نمی‌رسد راهی برای درخواست فضای ذخیره‌سازی بیشتر وجود داشته باشد.

در گذشته، اگر سایتی از آستانه خاصی از داده‌های ذخیره‌شده فراتر می‌رفت، مرورگر از کاربر می‌خواست اجازه استفاده از داده‌های بیشتری را بدهد. برای مثال، اگر مبدأ از بیش‌از ۵۰ مگابایت استفاده می‌کرد، مرورگر از کاربر درخواست می‌کرد تا ۱۰۰ مگابایت را برای ذخیره کردن مجاز کند، سپس دوباره در افزایش‌های ۵۰ مگابایتی درخواست می‌کرد.

امروزه، اکثر مرورگرهای جدید از کاربر درخواست نمی‌کنند و به سایت اجازه می‌دهند تا سقف سهمیه اختصاص‌یافته‌اش استفاده کند. به‌نظر می‌رسد Safari استثنا باشد، که وقتی از سهمیه فضای ذخیره‌سازی فراتر می‌رود، پیام‌واره‌ای نشان می‌دهد و برای افزایش سهمیه اختصاص‌داده‌شده اجازه می‌خواهد. اگر مبدأیی تلاش کند از سهمیه اختصاص‌یافته‌اش بیشتر استفاده کند، تلاش‌های بعدی برای نوشتن داده ناموفق خواهد بود.

چگونه می‌توانم بررسی کنم که چقدر فضای ذخیره‌سازی دردسترس است؟

در بسیاری از مرورگرها، می‌توانید از StorageManager API برای تعیین مقدار فضای ذخیره‌سازی دردسترس برای مبدأ و مقدار فضای ذخیره‌سازی مورداستفاده آن استفاده کنید. این میانای برنامه‌سازی کاربردی تعداد کل بایت‌های استفاده‌شده توسط IndexedDB و Cache API را گزارش می‌دهد و امکان محاسبه فضای ذخیره‌سازی باقی‌مانده تقریبی دردسترس را فراهم می‌کند.

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

باید خطاهای فراتر از سهمیه را بگیرید (به زیر مراجعه کنید). در برخی موارد، ممکن است سهمیه دردسترس از مقدار واقعی فضای ذخیره‌سازی دردسترس بیشتر باشد.

بازرسی

درطول توسعه، می‌توانید از DevTools مرورگرتان برای بازرسی انواع مختلف فضای ذخیره‌سازی و پاک کردن همه داده‌های ذخیره‌شده استفاده کنید.

ویژگی جدیدی در Chrome 88 اضافه شده است که به شما امکان می‌دهد سهمیه فضای ذخیره‌سازی سایت را در «قاب فضای ذخیره‌سازی» لغو کنید. این ویژگی به شما امکان می‌دهد دستگاه‌های مختلف را شبیه‌سازی کنید و رفتار برنامه‌هایتان را در سناریوهای دردسترس بودن کم دیسک آزمایش کنید. به Application و سپس Storage بروید، چارگوش انتخاب Simulate custom storage quota را فعال کنید، و هر عدد معتبری را برای شبیه‌سازی سهمیه فضای ذخیره‌سازی وارد کنید.

چگونه فراتر رفتن از سهمیه را مدیریت کنیم؟

وقتی از سهمیه فراتر می‌روید چه باید بکنید؟ مهم‌تر از همه، همیشه باید خطاهای نوشتن را، چه QuotaExceededError باشد یا چیز دیگری، شناسایی و مدیریت کنید. سپس، بسته به طراحی برنامه‌تان، تصمیم بگیرید که چگونه آن را مدیریت کنید. برای مثال، محتوایی را که مدت زیادی است به آن دسترسی پیدا نشده است حذف کنید، داده‌ها را براساس اندازه بردارید، یا راهی برای کاربران فراهم کنید تا آنچه را می‌خواهند حذف کنند انتخاب کنند.

وقتی از سهمیه دردسترس فراتر بروید، IndexedDB و Cache API هر دو خطای DOMError با نام QuotaExceededError را نشان می‌دهند.

IndexedDB

اگر مبدأ از سهمیه‌اش فراتر رفته باشد، تلاش‌ها برای نوشتن در IndexedDB ناموفق خواهد بود. کنترل‌کننده onabort() تراکنش فراخوانده می‌شود و رویدادی را ارسال می‌کند. رویداد شامل DOMException در ویژگی خطا خواهد بود. بررسی خطای name، 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
  }
};

Cache API

اگر مبدأ از سهمیه‌اش فراتر رفته باشد، تلاش‌ها برای نوشتن در Cache API با 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
  }
}

حکم تخلیه چگونه کار می‌کند؟

فضای ذخیره‌سازی وب به دو دسته «Best Effort» و «Persistent» تقسیم می‌شود. «بهترین تلاش» یعنی فضای ذخیره‌سازی می‌تواند توسط مرورگر بدون ایجاد وقفه برای کاربر پاک شود، اما برای داده‌های بلندمدت یا مهم دوام کمتری دارد. وقتی فضای ذخیره‌سازی کم باشد، فضای ذخیره‌سازی پایدار به‌طور خودکار پاک نمی‌شود. کاربر باید این فضای ذخیره‌سازی را به‌صورت دستی (ازطریق تنظیمات مرورگر) پاک کند.

به‌طور پیش‌فرض، داده‌های سایت (ازجمله IndexedDB،‏ Cache API، و غیره) در دسته «بهترین تلاش» قرار می‌گیرد، یعنی مگراینکه سایتی درخواست فضای ذخیره‌سازی دائمی کرده باشد، مرورگر ممکن است به‌صلاحدید خود داده‌های سایت را حذف کند، مثلاً وقتی فضای ذخیره‌سازی دستگاه کم باشد.

خط‌مشی تخلیه برای بهترین تلاش عبارت است از:

  • مرورگرهای مبتنی بر Chromium وقتی فضای مرورگر تمام شود، شروع به تخلیه داده‌ها می‌کنند و ابتدا همه داده‌های سایت را از مبدأی که اخیراً کمتر استفاده شده است پاک می‌کنند، سپس مبدأ بعدی، تا زمانی که مرورگر دیگر از حد مجاز فراتر نرود.
  • وقتی فضای دیسک دردسترس پر شود، Firefox شروع به تخلیه داده‌ها می‌کند و ابتدا همه داده‌های سایت را از مبدأی که اخیراً کمتر از همه استفاده شده است پاک می‌کند، سپس مبدأ بعدی، تا زمانی که مرورگر دیگر از حد مجاز فراتر نرود.
  • Safari قبلاً داده‌ها را حذف نمی‌کرد، اما اخیراً محدودیت جدید هفت‌روزه‌ای را برای همه فضاهای ذخیره‌سازی قابل‌نوشتن اعمال کرده است (به زیر مراجعه کنید).

از iOS و iPadOS نسخه ۱۳.۴ و Safari نسخه ۱۳.۱ در macOS، محدودیت هفت‌روزه‌ای برای همه فضاهای ذخیره‌سازی قابل‌نوشتن با اسکریپت وجود دارد، ازجمله IndexedDB، ثبت کارگر سرویس، و Cache API. این یعنی Safari پس‌از هفت روز استفاده از Safari، اگر کاربر با سایت تعامل نداشته باشد، همه محتوا را از حافظه نهان حذف می‌کند. این خط‌مشی تخلیه برای برنامه‌های وب پیشرو نصب‌شده که به صفحه اصلی اضافه شده‌اند اعمال نمی‌شود. برای جزئیات کامل، به مسدود کردن کامل کوکی‌های طرف سوم و موارد دیگر در وبلاگ WebKit مراجعه کنید.

سطل‌های فضای ذخیره‌سازی

ایده اصلی Storage Buckets API این است که به سایت‌ها امکان می‌دهد چندین دسته فضای ذخیره‌سازی ایجاد کنند، که در آن مرورگر می‌تواند هر دسته را مستقل از دسته‌های دیگر حذف کند. این ویژگی به توسعه‌دهندگان امکان می‌دهد اولویت‌بندی تخلیه را مشخص کنند تا مطمئن شوند ارزشمندترین داده‌ها حذف نمی‌شود.

پاداش: چرا باید از پوشاننده برای IndexedDB استفاده کرد

IndexedDB یک میانای برنامه‌سازی کاربردی سطح پایین است که قبل‌از استفاده به راه‌اندازی قابل‌توجهی نیاز دارد، که می‌تواند به‌ویژه برای ذخیره داده‌های با پیچیدگی کم دردسرساز باشد. برخلاف اکثر میاناهای برنامه‌سازی کاربردی مدرن مبتنی بر Promise، این میانا مبتنی بر رویداد است. پوشاننده‌های Promise مانند idb برای IndexedDB برخی‌از ویژگی‌های قدرتمند را پنهان می‌کنند، اما مهم‌تر از آن، سازوکار پیچیده (مانند تراکنش‌ها، نسخه‌بندی طرح‌واره) را که همراه با کتابخانه IndexedDB ارائه می‌شود، پنهان می‌کنند.

پاداش: SQLite Wasm

پس‌از منسوخ شدن و حذف Web SQL از Chrome، ‏Google با نگهدارندگان پایگاه داده محبوب SQLite همکاری کرد تا جایگزینی برای Web SQL مبتنی بر SQLite ارائه دهد. برای جزئیات نحوه استفاده از آن، SQLite Wasm در مرورگر با پشتیبانی Origin Private File System را بخوانید.

نتیجه‌گیری

روزهای فضای ذخیره‌سازی محدود و درخواست از کاربر برای ذخیره داده‌های بیشتر و بیشتر گذشته است. سایت‌ها می‌توانند به‌طور مؤثر همه منابع و داده‌هایی را که برای اجرا نیاز دارند ذخیره کنند. بااستفاده از StorageManager API می‌توانید تعیین کنید چقدر فضا دردسترس شما است و چقدر از آن استفاده کرده‌اید. و با فضای ذخیره‌سازی پایدار، می‌توانید از آن دربرابر تخلیه محافظت کنید، مگراینکه کاربر آن را بردارد.

منابع بیشتر

سپاس‌گزارم

با تشکر ویژه از جرید گودمن، فیل والتون، ایجی کیتامورا، دانیل مورفی، داروین هوانگ، جاش بل، مارین کرویسلبرینک، و ویکتور کوستان برای مرور این راهنما. با تشکر از ایجی کیتامورا، ادی عثمانی، و مارک کوهن که مقاله‌های اصلی را که این مقاله براساس آن‌ها است نوشتند. ایجی ابزار مفیدی به‌نام Browser Storage Abuser نوشت که برای اعتبارسنجی رفتار فعلی مفید بود. این تنظیم به شما امکان می‌دهد تا حد امکان داده ذخیره کنید و محدودیت‌های فضای ذخیره‌سازی را در مرورگرتان ببینید. Thanks to François Beaufort who did the digging into Safari to figure out its storage limits and to Thomas Steiner for adding information about the origin private file system, storage buckets, SQLite Wasm, and an overall content update in 2024.