モダンブラウザのストレージ容量まとめ

ストレージに関する調査とその結果

HTML5 Rocks

はじめに

HTML5 という言葉が聞かれるようになって最も大きな変化の一つがブラウザでのストレージ環境の充実です。従来であれば Cookie (もしくはプラグインで利用可能になった保存領域) 以外になかったストレージに、豊富な選択肢が用意されるようになりました。

最もポピュラーなのは WebStorage と呼ばれるキーバリュー型のストレージで、全てのメジャーブラウザの割と古いバージョンから実装されているため、既に広く利用されています。WebStorage には永久に保存される LocalStorage と、セッション中のみ保存される SessionStorage の 2 種類があります。

WebSQL Database はリレーショナル・データベースで、既に標準化作業が停止されているにも関わらず、スマートフォンブラウザの多くに搭載されている唯一の構造型データベースであるため、未だに重宝されています。

Indexed Database はオブジェクト型ストレージで、次世代ブラウザデータベースの本命として多くのブラウザに実装され、徐々に利用が広がっています。今後最も利用されるデータベースになるでしょう。

FileSystem API はブラウザ上に実現されるファイルシステムで、既に Chrome, Opera に実装されています。今のところ他のブラウザには実装されていませんが、慎重に仕様の議論が進められています。

Application Cache はシンプルページアプリケーションを想定した強力なキャッシュ機構です。いくつかの問題点が指摘され、現在 ServiceWorker と呼ばれる代替手段が検討されていますが、現在のところこれを超えるオフラインソリューションは存在しません。

今後ウェブアプリケーションがリッチ化するにつれ、さらにこれらブラウザ上のストレージの利用が拡大していくことが予想されます。

非常に便利なブラウザストレージですが、いずれも端末の保存領域を使うことから、自ずとその限界は存在するのですが、今まであまり語られることはありませんでした。
また、各利用者がひとつひとつのブラウザについて、何の前知識もなく調査を行うのは無駄の多い作業となります。この記事では各ブラウザにおけるストレージの実装状況について調査を行い、検証方法とその結果を紹介していきます。

概要

この調査を行うに当たり、手間を最小限に抑えるためのツールとして Browser Storage Abuser を制作しました。このページを表示しているブラウザ上で各ストレージにどのくらいのデータを保存することができるのかを限界まで試すことができます。

この記事では、このツールを使って得られた結果をまとめました。クイックリファレンスとしてご利用下さい。

Note: Opera ブラウザについては Chromium ベースとなり Chrome と同等の結果が得られたため、省略しております。

モバイル

Chrome Android Browser Firefox Safari iOS WebView
33 4.3 26 6, 7 6, 7
Application Cache up to quota Unlimited? 5MB, Unlimited 300MB? 100MB?
FileSystem up to quota
IndexedDB up to quota 5MB, Unlimited
WebSQL up to quota 200MB~ 5MB, 10MB, 25MB, 50MB 50MB
LocalStorage 10MB 2MB 10MB 5MB 5MB
SessionStorage 10MB Unlimited 5MB 5MB

デスクトップ

Chrome Firefox Safari IE IE
33 26 6, 7 9 10, 11
Application Cache up to quota 500MB, Unlimited Unlimited? 100MB?
FileSystem up to quota
IndexedDB up to quota 50MB, Unlimited 10MB, 250MB (~999MB)
WebSQL up to quota 5MB, 10MB, 50MB, 100MB, 500MB, 600MB, 700MB...
LocalStorage 10MB 10MB 5MB 10MB 10MB
SessionStorage 10MB Unlimited 10MB 10MB

詳細:実際のブラウザにおける実装

全体的な雰囲気がわかったところで各ブラウザごとの詳細と、デバッグ時の注意点などをご紹介していきましょう。

Chrome / Chrome for Android

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
Chrome 33 up to quota up to quota up to quota up to quota 10MB 10MB
Chrome for Android 33 up to quota up to quota up to quota up to quota 10MB 10MB

Chrome はここで挙げられている Application Cache、FileSystem API、IndexeDB、WebSQL、LocalStorage、SessionStorage 全てのストレージが利用できます。また、LocalStorage、SessionStorage を除く全てのストレージが Quota Management API で管理されるクオータに従うため、デバイスの残り容量に従って保存領域が変わってきます。残りの利用可能容量は Quota Management API を使ってプログラム上で取得することができます。なお、LocalStorage と SessionStorage にクオータが適用されないのは、非同期入出力ができないため、少量データでの利用が前提とされているためです。

現行の Quota のルールはこうです:

  • Temporary Storage (デフォルト) の場合、全てのストレージの合計でディスクの残り容量のうち 10% が利用可能です。クオータを超えた保存を行おうとした場合、古いオリジンのものから削除されます。
  • Persistent Storage を使った場合、任意のクオータをリクエストすることができます。ただし、現在の実装 (バージョン33) では、これが適用されるのは FileSystem API のみであり、他のストレージに関しては全て Temporary Storage の領域に保存されます。クオータを超えた保存を行おうとした場合は、エラーが発生します。

Quota Management API の 2014 年 1 月現在の実装についてはこちらの記事を参考にして下さい。Quota Management API は標準化が進められており、今後この実装は変更される予定です。

インスペクト方法

Chrome DevTools の Resources パネルをご覧頂ければ、Application Cache、IndexedDB、WebSQL、LocalStorage、SessionStorageについて、それぞれデータ内容まで含めてインスペクトすることができます。
FileSystem API について調査したい場合は:

  1. about:flags で "Enable Developer Tools experiments." をオン
  2. Chrome を再起動した上で DevTools の設定を開く
  3. "Settings" の "Experiments" パネルを開き、"FileSystem inspection" をチェック
  4. DevTools を開き直す

これで Resources パネルに他のストレージ同様 FileSystem の項目が見えるようになるはずです。

デスクトップ版のストレージは下記の方法で初期化することができます。

  1. chrome://settings/cookies を開く
  2. ドメインを指定
  3. 削除する

もしくは

  • DevTools の Resources パネル上で削除

Android 版のストレージは下記の方法で初期化することができます。

  1. "設定" を開く
  2. "コンテンツの設定" を開く
  3. "ウェブサイト設定" を開く
  4. 該当ドメインをタップ
  5. "保存したデータの消去" をタップ

なお、Chrome for Android でも DevTools を使ってデスクトップ版と同様の方法でインスペクトや削除を行うことができます。Chrome for Android でのデバッグ方法についてはこちらの記事が参考になります。

Firefox / Firefox for Android

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
Firefox 26 500MB, Unlimited 50MB, Unlimited 10MB
Firefox for Android 26 5MB, Unlimited 5MB, Unlimited 10MB

Firefox では Application Cache、IndexedDB、LocalStorage、SessionStorage が利用可能です。
LocalStorage と SessionStorage はそれぞれ 10MB が利用可能と記載していますが、実際は合計の容量の上限が 10MB となります。
IndexedDB は デスクトップ版が 50MB、モバイル版が 5MB が一時的な上限となり、ユーザーの許可が得られれば、その後の保存領域は無制限となります。
Application Cache はデスクトップ版が 500MB、モバイル版が 5MB が一時的な上限となり、こちらもユーザーの許可が得られれば、その後の保存領域は無制限となります。

なお、about:config ページの dom.indexedDB.warningQuota を変更することで IndexedDB の、browser.cache.offline.capacity を変更することで Application Cache の上限拡大の許可を求める容量は、それぞれユーザー自身の手で変更することができます。

Note: IndexedDB の データは圧縮して BLOB として sqlite に保存されるため、元のデータのサイズがそのままのクオータを消費するわけではないことに注意して下さい。

インスペクト方法

実際に使われている容量は、デスクトップ版では Tahoe Data Manager を使ってインスペクトできますが、調査時の最新版 26 では Storage タブが利用できなくなっているようです。
データは sqlite に保存されるため、一般的なツールを使って中身を調査することもできます。sqlite ファイルは各 OS のプロファイルが収められているフォルダの idb 内に存在します。

Windows: %AppData%\Mozilla\Firefox\Profiles\xxxxxxxx.default
Mac: ~/Library/Application Support/Firefox/Profiles/xxxxxxxx.default

SQLite Manager という Add-on を使えばインスペクトがやりやすくなるかもしれません。

また、サイトごとに与えたクオータの上限を外す許可は about:permissions で初期化可能です。

Android 版については、残念ながら現時点でインスペクトやサイトごとのデータ削除を行う方法はないようです。デバッグ方法についてはこのドキュメントが参考になります。

Android Browser

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
Android Browser Unlimited? 200MB~? 2MB Unlimited

Android の Browser では Application Cache、WebSQL、LocalStorage、SessionStorage が利用可能です。
LocalStorage は他と比較して若干少なく、上限は 2MB となっています。SessionStorage はメモリ上に保存するため、上限なく保存できるようです。
WebSQL は一度のトランザクションで保存しようとする容量が 500KB 前後でクオータエラーが発生しますが、トランザクションを分ければ追加保存が可能なようです。200MB 程度まで試しましたが、残念ながらブラウザ自体がクラッシュしてしまいました。
また未確認ですが、Application Cache では無制限にデータを保存できると言われています。

インスペクト方法

Android Browser に保存されたデータは weinre というオープンソースのツールを使うことでWebSQL、LocalStorage、SessionStorage をインスペクトすることができます。

データは weinre 上で個別に削除できますが、Android 単体では下記の手順で消すことができます。ドメインごとに削除することはできません。

システムの "Settings" > "Apps" > "Browser" を開き、"Clear data" をタップ

Safari / Safari for iOS

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
Safari 6, 7 Unlimited? 5MB, 10MB, 50MB, 100MB, 500MB, 600MB, 700MB... 5MB Unlimited
Mobile Safari 6, 7 300MB? 5MB, 10MB, 25MB, 50MB 5MB 5MB
iOS WebView 100MB? 50MB 5MB 5MB

デスクトップ版 Safari では Application Cache、WebSQL、LocalStorage、SessionStorage が利用可能です。
LocalStorage は 5MB と標準的な容量が利用可能ですが、SessionStorage はメモリ上に保存するため、上限なく保存できるようです。
WebSQL に関しては一定の容量に達するごとにユーザーに許可を求める方式を採用しており、500MB 以上は 100MB 消費するごとに許可を求めます。ただし openDatabase の実行時に予め 5MB を超える容量を要求すると、最初にデータベースを構築する時点で、要求された容量に最も近い上限値での許可をユーザーに求め、それを超えた場合のみさらに許可を求める仕様になっています。

モバイル版 Safari もルールは似ていますが、上限が異なります。LocalStorage の上限が 5MB であるのと同様、SessionStorage の上限も 5MB になっています。また、WebSQL の場合、5MB, 10MB, 50MB とユーザーに許可を求めますが、50MB 以上に増やすことはできません。Application Cache では 10MB という情報を見つけましたが、筆者が試したところ、300MB 程度は問題なく保存することができました。

Note: iOS 7 〜 7.0.4 の Safari では、データベース作成時に 5MB 以上をリクエストすると例外が発生するというバグがあります。これを回避するには、データベース作成時に 5MB 以下をリクエストし、容量の追加はブラウザに任せ、ユーザーの許可を得る他ありません。ユーザーにとっては迷惑な話ですが、現状これ以外の回避方法はないようです。
Update: この問題は iOS 7.1 で解消されたようです。詳細はこちらをご覧下さい。

iOS では WebView と呼ばれるネイティブコンポーネントの中で利用可能なブラウザが存在しますが、その場合は WebSQL で 50MB が固定の上限になっており、ユーザーに許可を求めることはありません。

インスペクト方法

デスクトップ版 Safari では Web Inspector を使うことで LocalStorage, SessionStorage, WebSQL のインスペクトを行うことができます。

モバイル版 Safari では残念ながら WebSQL のインスペクトを行うことはできませんが、LocalStorage、SessionStorage のインスペクトは可能です。モバイル版 Safari のデバッグ方法についてはこちらをご覧下さい。

Internet Explorer

AppCache FileSystem IndexedDB WebSQL LocalStorage SessionStorage
IE 9 10MB 10MB
IE 10, 11 100MB? 10MB, 250MB (~999MB) 10MB 10MB

Internet Explorer では、9 以降は LocalStorage、SessionStorage が、10 以降では Application Cache と IndexedDB も合わせて利用可能になっています。
LocalStorage, SessionStorage ではそれぞれ 10MB まで保存が可能です。
IndexedDB は、デフォルトでは 10MB に達した時点でユーザーに許可を求め、ドメインごとに 250MB まで利用可能になっていますが、下記の方法でユーザーが設定を変更すれば、最大 999MB まで保存することができます。
Application Cache では 100MB を超えた辺りでエラーが発生しましたが、これがクオータによるものかどうかははっきりしませんでした。

インスペクト方法

残念ながら Internet Explorer ではどのストレージもインスペクトする方法は提供されていないようです。
ただし、Internet Options の General タブにある Browsing history から Settings をクリックし、Caches and databases タブを選択することで、ドメインごとにデータを削除することができます。

クオータがオーバーした場合の対処方法

それでは、実際にクオータがオーバーしてしまった場合、どうすればよいのでしょうか?もちろんどういう動作を期待するかはアプリケーションの仕様によりますので、一概に決めることはできません。しかし、コードレベルで何が起きるかを知る機会はなかなかないと思いますので、ここではサンプルコードを示しながら各ストレージごとの挙動を示します。

Application Cache

最もデバッグが難しいのが Application Cache です。いくつかのブラウザではエラーの理由をコンソールに表示してくれますが、エラーイベントに DOMError オブジェクトが紐付けられていないため、プログラム上で理由を知る方法はありません。

var appCache = window.applicationCache;
appCache.addEventListener('error', function(e) {
  // It's diffulct to tell cause of this error since there's no DOMError object associated with callback argument.
});

FileSystem API

FileSystem でクオータがオーバーした場合、いくつかのポイントでエラーが発生する可能性があります。

FileEntry または DirectoryEntrygetFile(), copyTo(), moveTo() 関数の場合、第二引数に与えられた関数に FileError を引数として返します。
FileWriterwrite() 関数の場合、onerror()ProgressEvent を引数として返します。FileErrorProgressEvent.target.error に格納されています。

いずれの場合も FileError オブジェクト (将来的に DOMError オブジェクトに変更される予定) の name プロパティが "QuotaExceededError" かどうかでクオータエラーかどうかを判定して下さい。

なお、FileSystem API が WebWorker で使われる同期バージョンの API (FileSystemSync) の場合、DOMException が発生します。

filesystem.root.getDirectory(dirName, {create:true, exclusive:false},
function getDirectorySuccess(dirEntry) {
  dirEntry.getFile(fileName, {create:true, exclusive:false},
  function getFileSuccess(fileEntry) {
    fileEntry.createWriter(function(writer) {
      writer.onwriteend = function() {
        ...
      };
      writer.onerror = function(progressEvent) {
        var fileError = progressEvent.target.error;
        if (fileError.name === 'QuotaExceededError') {
          // Fallback code comes here.
        }
      };
      writer.write(blob);
    });
  },
  // argument will be DOMError in the future
  function getFileError(fileError) {
    if (fileError.name == 'QuotaExceededError') {
      // Fallback code comes here.
    }
  });
},
// argument will be DOMError in the future
function getDirectoryError(fileError) {
  if (fileError.name == 'QuotaExceededError') {
    // Fallback code comes here.
  }
});

IndexedDB API

IndexedDB API でクオータがオーバーした場合、エラーはトランザクションの onabort()Event を引数として返ってきます。Event.target.error に格納されている DOMError オブジェクトの name プロパティが "QuotaExceededError" かどうかでクオータエラーかどうかを判定して下さい。

どのブラウザでも、ストレージサイズの拡張がユーザーから許可されなかった場合、もしくは実際に上限に達した場合のみこのエラーが呼び出され、許可された場合はそのままトランザクションを続行します。

var transaction = idb.transaction(['entries'], 'readwrite');
transaction.oncomplete = function(e) {
  ...
};
transaction.onerror = function(e) {
  ...
};
transaction.onabort = function(event) {
  var error = event.target.error; // DOMError
  if (error.name == 'QuotaExceededError') {
    // Fallback code comes here
  }
};
var req = transaction.objectStore('entries').put(data);
req.onerror = function(e) {
  // Quota error won't call this function
};

WebSQL Database

WebSQL は若干トリッキーです。WebSQL でクオータがオーバーした場合、エラーはトランザクションの第二引数に与えられた関数に SQLError を引数として返します。SQLError オブジェクトの code プロパティが SQLError.QUOTA_ERR と一致するかどうかでクオータエラーかどうかを判定して下さい。

ただし Safari の場合、ブラウザがユーザーにストレージサイズの拡張を問い合わせると、ユーザーの応答が何であれこのエラーを返してしまいます。また、開発者はユーザーがストレージサイズの拡張が許可されたかどうかを知る術はありません。

db.transaction(function onTransaction(t) {
  t.executeSql('INSERT INTO entries (name, size, date, payload) VALUES(?, ?, ?, ?)', data,
  function onExecuteSqlCallback(t, results) {
    ...
  },
  function onExecuteSqlError(t, e) {
    // Returning true to rollback, false to continue the transaction;
    return false;
  });
},
function onTransactionError(sqlError) {
  // Safari calls this function regardless if user permits more quota or not.
  // And there's no way for a developer to know user's reaction.
  if (sqlError.code === sqlError.QUOTA_ERR) {
    // Start over the transaction again to check if user permitted or not.
  }
},
function onTransactionSuccess() {
  ...
});

LocalStorage

LocalStorage, SessionStorage の場合、同期 API ですので、try catch で例外処理を行います。DOMExceptionname プロパティが "QuotaExceededError"、または "NS_ERROR_DOM_QUOTA_REACHED" (Firefox の場合) の場合、クオータがオーバーしたと判断することができます。

try {
  localStorage.setItem(data.name, JSON.stringify(data));
} catch(domException) {
  if (domException.name === 'QuotaExceededError' ||
      domException.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
    // Fallback code comes here.
  }
}

最後に

以上のように、ブラウザ上のストレージの上限は、各ブラウザごとにそれぞれ特有のルールが適用されています。クオータが上限に達してもエラーにならず、うまくフォールバックするロジックを予め組み込んでおくことが重要です。
また、現在仕様の議論が行われている ServiceWorker新しい Quota Management API についても調査をおすすめします。これらは、この記事に書かれた制限に劇的な変化をもたらす可能性があります。

最後に、もし間違った情報や新しい情報を見つけた場合は、ぜひこの記事に対して Pull Request を送って下さい。

Comments

0