ストレージ API

アプリ開発のほぼすべての側面には、データの送受信に関する要素が含まれます。まず、アプリの設計と実装に MVC フレームワークを使用すると、データがそのデータに対するアプリのビューから完全に分離されます(MVC アーキテクチャをご覧ください)。

アプリがオフラインのときのデータの処理方法も考慮する必要があります(オフラインファーストをご覧ください)。 このドキュメントでは、ローカルでデータを送受信、保存するためのストレージ オプションについて簡単に紹介します。ドキュメントの残りの部分では、Chrome の File System API と Sync File System API の使用方法について説明します(fileSystem APIsyncFileSystem API もご覧ください)。

ストレージ オプション

パッケージ化されたアプリは、データの送受信にさまざまなメカニズムを使用します。外部データ(リソース、ウェブページ)については、コンテンツ セキュリティ ポリシー(CSP)を認識する必要があります。Chrome 拡張機能と同様に、クロスオリジンの XMLHttpRequests を使用してリモート サーバーと通信できます。また、外部ページを分離して、アプリの他の部分を保護することもできます(外部ウェブページを埋め込むをご覧ください)。

データをローカルに保存する場合、Chrome Storage API を使用して少量の文字列データを保存し、IndexedDB を使用して構造化データを保存できます。IndexedDB を使用すると、JavaScript オブジェクトをオブジェクト ストアに保持し、ストアのインデックスを使用してデータをクエリできます(詳細については、HTML5 Rock の Simple Todo List チュートリアルをご覧ください)。バイナリデータなど、他のすべてのタイプのデータには、Filesystem API と Sync Filesystem API を使用します。

Chrome の Filesystem API と Sync Filesystem API は HTML5 FileSystem API を拡張したものです。Chrome の Filesystem API を使用すると、アプリはユーザーのローカル ファイル システムのサンドボックス化されたセクションの作成、読み取り、ナビゲーション、書き込みを行うことができます。たとえば、写真共有アプリは、Filesystem API を使用して、ユーザーが選択した写真の読み取りと書き込みを行うことができます。

Chrome の Sync Filesystem API を使用すると、アプリでユーザーの Google ドライブにデータを保存して同期できるため、異なるクライアントで同じデータを使用できます。たとえば、クラウドを利用したテキスト エディタ アプリは、新しいテキスト ファイルをユーザーの Google ドライブ アカウントに自動的に同期できます。ユーザーが新しいクライアントでテキスト エディタを開くと、Google ドライブはそのテキスト エディタのそのインスタンスに新しいテキスト ファイルをプッシュします。

Chrome Filesystem API を使用する

ファイル システムの権限の追加

Chrome の File System API を使用するには、「fileSystem」権限をマニフェストに追加して、永続データを保存する権限をユーザーから取得できるようにする必要があります。

"permissions": [
  "...",
  "fileSystem"
]

ファイル選択のユーザー オプション

ユーザーは、いつもと同じ方法でファイルを選択することを期待しています。少なくとも、[ファイルを選択] ボタンと標準のファイル選択ツールが必要です。アプリでファイル処理を多用するアプリの場合は、ドラッグ&ドロップも実装する必要があります(以下の説明に加えて、ネイティブ HTML5 のドラッグ&ドロップもご覧ください)。

fileEntry のパスの取得

ユーザーが選択したファイル(fileEntry)のフルパスを取得するには、getDisplayPath() を呼び出します。

function displayPath(fileEntry) {
  chrome.fileSystem.getDisplayPath(fileEntry, function(path) {
    console.log(path)
  });
}

ドラッグ&ドロップの実装

ドラッグ&ドロップによる選択を実装する必要がある場合は、filesystem-access サンプルのドラッグ&ドロップ ファイル コントローラ(dnd.js)から始めることをおすすめします。コントローラは、ドラッグ&ドロップで DataTransferItem からファイル エントリを作成します。この例では、fileEntry は最初にドロップされたアイテムに設定されています。

var dnd = new DnDFileController('body', function(data) {
  var fileEntry = data.items[0].webkitGetAsEntry();
  displayPath(fileEntry);
});

ファイルを読み取る

次のコードは、ファイル(読み取り専用)を開き、FileReader オブジェクトを使用してテキストとして読み取ります。ファイルが存在しない場合は、エラーがスローされます。

var chosenFileEntry = null;

chooseFileButton.addEventListener('click', function(e) {
  chrome.fileSystem.chooseEntry({type: 'openFile'}, function(readOnlyEntry) {

    readOnlyEntry.file(function(file) {
      var reader = new FileReader();

      reader.onerror = errorHandler;
      reader.onloadend = function(e) {
        console.log(e.target.result);
      };

      reader.readAsText(file);
    });
    });
});

ファイルの作成

ファイルを書き込む場合の一般的な用途には、[保存] と [名前を付けて保存] の 2 つがあります。次のコードは、読み取り専用の chosenFileEntry から writableEntry を作成し、選択したファイルをそこに書き込みます。

 chrome.fileSystem.getWritableEntry(chosenFileEntry, function(writableFileEntry) {
    writableFileEntry.createWriter(function(writer) {
      writer.onerror = errorHandler;
      writer.onwriteend = callback;

    chosenFileEntry.file(function(file) {
      writer.write(file);
    });
  }, errorHandler);
});

次のコードは、「名前を付けて保存」機能を使用して新しいファイルを作成し、writer.write() メソッドを使用して新しい blob をファイルに書き込みます。

chrome.fileSystem.chooseEntry({type: 'saveFile'}, function(writableFileEntry) {
    writableFileEntry.createWriter(function(writer) {
      writer.onerror = errorHandler;
      writer.onwriteend = function(e) {
        console.log('write complete');
      };
      writer.write(new Blob(['1234567890'], {type: 'text/plain'}));
    }, errorHandler);
});

Chrome Sync Filesystem API を使用する

同期可能なファイル ストレージを使用すると、返されたデータ オブジェクトを FileSystem API のローカル オフライン ファイル システムと同じように操作できますが、そのデータを Google ドライブに追加(自動)同期できます。

同期ファイル システムの権限の追加

Chrome の Sync Filesystem API を使用するには、「syncFileSystem」権限をマニフェストに追加して、永続データを保存して同期するための権限をユーザーから取得できるようにする必要があります。

"permissions": [
  "...",
  "syncFileSystem"
]

同期可能なファイル ストレージの開始

アプリで同期可能なファイル ストレージを開始するには、syncFileSystem.requestFileSystem を呼び出します。このメソッドは、Google ドライブに基づく同期可能なファイル システムを返します。次に例を示します。

chrome.syncFileSystem.requestFileSystem(function (fs) {
   // FileSystem API should just work on the returned 'fs'.
   fs.root.getFile('test.txt', {create:true}, getEntryCallback, errorCallback);
});

ファイルの同期ステータスについて

syncFileSystem.getFileStatus を使用して、現在のファイルの同期ステータスを取得します。

chrome.syncFileSystem.getFileStatus(entry, function(status) {...});

ファイル同期ステータスの値は、'synced''pending''conflicting' のいずれかです。「同期済み」はファイルが完全に同期されていることを表します。Google ドライブと同期されていない保留中のローカル変更はありません。ただし、Google ドライブ側の保留中の変更がまだ取得されていない場合もあります。

「保留中」は、Google ドライブに同期されていない保留中の変更があることを意味します。アプリがオンラインで実行されている場合、ローカルの変更は(ほぼ)すぐに Google ドライブに同期され、syncFileSystem.onFileStatusChanged イベントは 'synced' ステータスとともに発生します(詳細については以下をご覧ください)。

syncFileSystem.onFileStatusChanged は、ファイルのステータスが 'conflicting' に変わると呼び出されます。「競合」は、ローカル ストレージと Google ドライブの両方で競合する変更があることを意味します。ファイルがこの状態になるのは、競合解決ポリシーが 'manual' に設定されている場合のみです。デフォルトのポリシーは 'last_write_win' であり、競合は単純な最後の書き込み優先ポリシーによって自動的に解決されます。システムの競合解決ポリシーは、syncFileSystem.setConflictResolutionPolicy によって変更できます。

競合解決ポリシーが 'manual' に設定されていて、ファイルが 'conflicting' 状態になっても、アプリは引き続きローカル オフライン ファイルとしてファイルを読み書きできますが、変更は同期されず、競合が解決されるまで、他のクライアントで行われたリモート変更からファイルが切り離されます。競合を解決する最も簡単な方法は、ローカル バージョンのファイルを削除するか、名前を変更することです。これにより、リモートのバージョンが強制的に同期され、競合状態が解決されて、'synced' ステータスとともに onFileStatusChanged イベントが発行されます。

同期ステータスの変更をリッスンする

syncFileSystem.onFileStatusChanged イベントは、ファイルの同期ステータスが変更されると発生します。たとえば、ファイルに保留中の変更があり、「保留」状態であるとします。変更が同期される間、アプリがオフライン状態になっていた可能性があります。同期サービスが保留中のローカル変更を検出して Google ドライブにアップロードすると、onFileStatusChanged イベントと値 { fileEntry:a fileEntry for the file, status: 'synced', action: 'updated', direction: 'local_to_remote' } が発生します。

同様に、ローカル アクティビティに関係なく、同期サービスは別のクライアントによるリモート変更を検出し、その変更を Google ドライブからローカル ストレージにダウンロードすることがあります。リモートの変更が新しいファイルの追加に対するものである場合、次の値を持つイベント { fileEntry: a fileEntry for the file, status: 'synced', action: 'added', direction: 'remote_to_local' } が発生します。

ローカル側とリモート側の両方で同じファイルに対して競合する変更があり、競合解決ポリシーが 'manual' に設定されている場合、ファイルのステータスは conflicting の状態に変更され、同期サービスとの接続が解除され、競合が解決されるまで同期されません。この場合、次の値を持つイベント { fileEntry: a fileEntry for the file, status: 'conflicting', action: null, direction: null } が発生します。

ステータスの変化に応答する、このイベントのリスナーを追加できます。たとえば、Chrome Music Player アプリは、Google ドライブから同期されたものの、特定のクライアントのユーザーのローカル ストレージにインポートされていない新しい音楽をリッスンします。見つかった音楽はすべてそのクライアントに同期されます。

chrome.syncFileSystem.onFileStatusChanged.addListener(function(fileInfo) {
  if (fileInfo.status === 'synced') {
    if (fileInfo.direction === 'remote_to_local') {
      if (fileInfo.action === 'added') {
        db.add(fileInfo.fileEntry);
      } else if (fileInfo.action === 'deleted') {
        db.remove(fileInfo.fileEntry);
      }
    }
  }
});

API の使用状況を確認する

API で使用されているデータの量を確認するには、アプリのサンドボックス化されたローカル ディレクトリまたは syncFileSystem.getUsageAndQuota によって返された使用量バイト数をクエリします。

chrome.syncFileSystem.getUsageAndQuota(fileSystem, function (storageInfo) {
   updateUsageInfo(storageInfo.usageBytes);
   updateQuotaInfo(storageInfo.quotaBytes);
});

また、ユーザーの同期バックエンド サービスのストレージ(Google ドライブ内)を確認することもできます。同期されたファイルは、非表示の Google ドライブ フォルダ(Chrome Syncable FileSystem)に保存されます。このフォルダは [マイドライブ] リストには表示されませんが、検索ボックスでフォルダ名を検索するとアクセスできます。(リモート フォルダのレイアウトでは、リリース間の後方互換性は保証されません)。