ウェブ向けのストレージ

ブラウザにデータを保存するためのさまざまな方法があります。お客様のニーズに最適なオプションをお選びください。

インターネット接続は不安定であったり、存在しない場合があります。そのため、プログレッシブ ウェブアプリでは、オフライン サポートと信頼性の高いパフォーマンスが一般的な機能となっています。完全なワイヤレス環境であっても、キャッシュやその他のストレージ技術を適切に使用することで、ユーザー エクスペリエンスが大幅に向上する可能性があります。静的なアプリケーション リソース(HTML、JavaScript、CSS、画像など)とデータ(ユーザーデータ、ニュース記事など)をキャッシュに保存するには、いくつかの方法があります。では、どちらのソリューションが最適なのでしょうか。どれくらい保存できますか?強制排除を防ぐにはどうすればよいですか?

何を使用すればよいですか?

リソースの保存に関する一般的な推奨事項は次のとおりです。

IndexedDB と Cache Storage API は、最新のすべてのブラウザでサポートされています。どちらも非同期で、メインスレッドをブロックしません。window オブジェクト、ウェブワーカー、Service Worker からアクセスできるため、コード内の任意の場所で簡単に使用できます。

他のストレージ メカニズムについてはどうですか。

ブラウザで使用できるストレージ メカニズムは他にもいくつかありますが、使用に制限があり、重大なパフォーマンスの問題を引き起こす可能性があります。

SessionStorage はタブ固有で、そのタブの有効期間に設定されます。これは、IndexedDB キーなど、セッション固有の情報を少量保存する場合に便利です。同期処理であり、メインスレッドをブロックするため、慎重に使用する必要があります。容量は約 5 MB に制限されており、文字列のみを含めることができます。タブ固有であるため、ウェブワーカーやサービス ワーカーからアクセスすることはできません。

LocalStorage は同期的であり、メインスレッドをブロックするため、使用しないでください。容量は約 5 MB に制限されており、文字列のみを含めることができます。ウェブワーカーや Service Worker から LocalStorage にはアクセスできません。

Cookie にはさまざまな用途がありますが、保存には使用しないでください。Cookie はすべての HTTP リクエストとともに送信されるため、少量のデータを保存すると、すべてのウェブ リクエストのサイズが大幅に増加します。これらは同期的であり、ウェブワーカーからはアクセスできません。LocalStorage や SessionStorage と同様に、Cookie は文字列のみに制限されます。

File System API と FileWriter API は、サンドボックス化されたファイル システムに対してファイルを読み書きするためのメソッドを提供します。非同期ですが、Chromium ベースのブラウザでのみ使用できるため、おすすめしません。

File System Access API は、ユーザーがローカル ファイル システム上のファイルを簡単に読み取り、編集できるように設計されています。ページでローカル ファイルの読み取りや書き込みを行うには、事前にユーザーが権限を付与する必要があります。権限はセッション間で維持されません。

WebSQL は使用しないでください。既存の使用状況を IndexedDB に移行する必要があります。ほぼすべての主要なブラウザでサポートが終了しました。W3C は 2010 年に Web SQL 仕様のメンテナンスを停止し、今後の更新は予定されていません。

アプリケーション キャッシュは使用しないでください。既存の使用状況は Service Worker と Cache API に移行してください。この API は非推奨になっており、今後ブラウザではサポートされなくなります。

保存できる容量

少なくとも数百 GB、場合によっては数百 GB 以上になります。ブラウザの実装はさまざまですが、利用可能なストレージ容量は通常、デバイスで利用できるストレージ容量によって異なります。

  • Chrome では、ブラウザが合計ディスク容量の 80% まで使用できます。送信元は合計ディスク容量の最大 60% を使用できます。StorageManager API を使用して、使用可能な最大割り当てを確認できます。他の Chromium ベースのブラウザは異なる場合があります。
    • シークレット モードでは、送信元が使用できる保存容量が合計ディスク容量の約 5% まで削減されます。
    • ユーザーが Chrome で [ウィンドウをすべて閉じるときに Cookie とサイトデータを削除する] を有効にしている場合、保存容量は最大で約 300 MB に大幅に削減されます。
    • Chrome の実装について詳しくは、PR #3896 をご覧ください。
  • Internet Explorer 10 以降では、最大 250 MB を保存でき、使用量が 10 MB を超えるとユーザーにプロンプトが表示されます。
  • Firefox では、ブラウザは空きディスク容量の 50% まで使用できます。eTLD+1 グループ(例:example.comwww.example.comfoo.bar.example.com など)では、最大 2 GB まで使用できますStorageManager API を使用して、使用可能な空き容量を確認できます。
  • Safari(パソコンとモバイルの両方)では約 1 GB の容量を使用できます。上限に達すると、Safari からプロンプトが表示され、200 MB 単位で上限が引き上げられます。この件に関する公式ドキュメントは見つかりませんでした。
    • モバイル Safari でホーム画面に PWA を追加すると、新しいストレージ コンテナが作成されているように見え、PWA とモバイル Safari の間で何も共有されません。インストール済みの PWA の割り当てに達すると、追加のストレージをリクエストすることはできません。

これまでは、サイトが保存データの特定のしきい値を超えると、ブラウザはユーザーにデータの追加を許可するよう求めるものでした。たとえば、送信元で 50 MB を超えるデータが使用されている場合、ブラウザはユーザーに最大 100 MB の保存を許可するよう求め、50 MB 単位で再度リクエストします。

現在、ほとんどの最新のブラウザはユーザーにプロンプトを表示せず、サイトは割り当てられた割り当てまで使用できます。例外は 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.`);
}

StorageManager はまだすべてのブラウザに実装されているわけではないため、使用する前に機能検出を行う必要があります。利用可能な場合でも、割り当て超過エラーを検出する必要があります(以下を参照)。場合によっては、使用可能な割り当てが実際の保存容量を超えることがあります。

検査

開発中は、ブラウザの DevTools を使用してさまざまなストレージ タイプを検査し、保存されているすべてのデータを簡単に消去できます。

Chrome 88 では、ストレージ ペインでサイトの保存容量をオーバーライドできる新機能が追加されました。この機能を使用すると、さまざまなデバイスをシミュレートし、ディスクの可用性が低い場合のアプリの動作をテストできます。[アプリケーション]、[ストレージ] の順に移動し、[カスタム ストレージ割り当てをシミュレート] チェックボックスをオンにして、保存容量をシミュレートするために有効な数値を入力します。

DevTools の [Storage] ペイン。

この記事の作業中に、できる限り多くのストレージを迅速に使用するためのシンプルなツールを作成しました。さまざまなストレージ メカニズムを試し、すべての割り当てを使用したときにどうなるかを確認する迅速かつ簡単な方法です。

割り当て量の超過に対処する方法

保存容量を超過した場合の対応最も重要なことは、QuotaExceededError など、書き込みエラーを常にキャッチして処理する必要があることです。次に、アプリの設計に応じて処理方法を決定します。たとえば、長期間アクセスされていないコンテンツを削除する、サイズに基づいてデータを削除する、削除するものをユーザーが選択できるようにする、などです。

IndexedDB と Cache API はどちらも、使用可能な割り当てを超えると、QuotaExceededError という名前の DOMError をスローします。

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

キャッシュ 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
  }
}

エビクションの仕組み

ウェブ ストレージは、「ベスト エフォート」と「永続」の 2 つのバケットに分類されます。ベスト エフォートでは、ユーザーを中断することなくブラウザがストレージを消去できますが、長期的なデータや重要なデータの耐久性は低くなります。永続ストレージは、ストレージが少なくても自動的には消去されません。ユーザーが(ブラウザの設定を介して)このストレージを手動で消去する必要があります。

デフォルトでは、サイトのデータ(IndexedDB、Cache API など)はベスト エフォート カテゴリに分類されます。つまり、サイトが永続ストレージをリクエストしない限り、ブラウザは独自の裁量でサイトデータを削除することがあります(デバイスのストレージが少ない場合など)。

ベスト エビクションのエビクション ポリシーは次のとおりです。

  • Chromium ベースのブラウザは、ブラウザのスペースが不足するとデータの削除を開始し、ブラウザが制限を超えなくなるまで、最も長い間使用されていないオリジンからすべてのサイトデータを消去します。
  • Internet Explorer 10 以降では、データが強制排除されることはありませんが、オリジンによる書き込みは行われなくなります。
  • Firefox は、使用可能なディスク容量がいっぱいになるとデータの削除を開始し、ブラウザが制限を超えなくなるまで、最も長い間使用されていないオリジンからすべてのサイトデータを消去します。
  • Safari ではこれまでデータの削除はありませんでしたが、最近、すべての書き込み可能なストレージに 7 日間の上限が新たに実装されました(以下を参照)。

iOS、iPadOS 13.4、macOS 版 Safari 13.1 以降では、IndexedDB、Service Worker の登録、Cache API など、すべてのスクリプトの書き込み可能なストレージに 7 日間の上限があります。つまり、Safari で 7 日間使用し、その後でユーザーがサイトを操作しない場合、すべてのコンテンツがキャッシュから削除されます。このエビクション ポリシーは、ホーム画面に追加されているインストール済みの PWA には適用されません。詳しくは、WebKit ブログのサードパーティ Cookie の完全なブロックなどをご覧ください。

参考: IndexedDB にラッパーを使用する理由

IndexedDB は、使用前にかなりのセットアップが必要な低レベルの API であり、単純なデータを保存する場合は特に手間がかかります。最新の Promise ベースの API とは異なり、これはイベントベースです。IndexedDB の idb のような Promise ラッパーは高度な機能の一部を非表示にしますが、さらに重要なこととして、IndexedDB ライブラリに付属する複雑な機能(トランザクション、スキーマのバージョニングなど)を隠します。

おわりに

ストレージが限られ、保存するデータが増え続ける時代は終わりました。サイトは、実行に必要なすべてのリソースとデータを効果的に保存できます。StorageManager API を使用すると、使用可能な容量と使用量を確認できます。永続ストレージを使用すると、ユーザーが削除しない限り、エビクションから保護できます。

参考情報

ありがとう

この記事をレビューしてくれた Jarryd Goodman、Phil Walton、Eiji Kitamura、Daniel Murphy、Darwin Huang、Josh Bell、Marijn Kruisselbrink、Victor Costan に感謝します。原稿の執筆は Eiji Kitamura、Addy Osmani、Marc Cohen でした。Eiji は、Browser Storage Abuser という便利なツールを作成しました。これは、現在の動作の検証に役立ちました。できるだけ多くのデータを保存し、ブラウザでストレージの上限を確認できます。Safari を調べて保存容量の上限を 調べてくれた Francois Beaufort に感謝します。

ヒーロー画像: Guillaume Bolduc - Unsplash より