アプリ向けの高パフォーマンス ストレージ: Storage Foundation API

ウェブ プラットフォームでは、ウェブ向けに微調整された高性能アプリケーションを構築するために必要なツールがデベロッパーに提供されるようになっています。特に、WebAssembly(Wasm)は高速で強力なウェブ アプリケーションへの扉を開き、Emscripten などのテクノロジーにより、デベロッパーはウェブ上でテスト済みのコードを再利用できるようになりました。この可能性を最大限に活用するには、デベロッパーがストレージに関して同じパワーと柔軟性を備えている必要があります。

ここで Storage Foundation API の出番です。Storage Foundation API は、高速で制約のない新しいストレージ API です。この API を使用すると、パフォーマンスの高いデータベースの実装や、大きな一時ファイルの適切な管理など、ウェブで新たに必要とされているユースケースを実現できます。この新しいインターフェースにより、デベロッパーはウェブに「独自のストレージを持ち込む」ことができ、ウェブとプラットフォーム固有のコード間の機能のギャップを減らすことができます。

Storage Foundation API は、非常に基本的なファイル システムに似た設計になっています。汎用的でシンプルかつパフォーマンスに優れたプリミティブを提供することで、デベロッパーがより高レベルのコンポーネントを構築できる柔軟性を実現しています。アプリケーションはニーズに最適なツールを活用し、使いやすさ、パフォーマンス、信頼性の適切なバランスを見つけることができます。

ウェブに別のストレージ API が必要なのはなぜですか?

ウェブ プラットフォームでは、デベロッパー向けに複数のストレージ オプションが用意されています。これらのオプションはそれぞれ、特定のユースケースを想定して構築されています。

  • これらのオプションの中には、CookiesessionStorage メカニズムと localStorage メカニズムで構成される Web Storage API など、ごく少量のデータしか保存できないため、この提案と明らかに重複しないものもあります。
  • File and Directory Entries APIWebSQL など、さまざまな理由で他のオプションはすでに非推奨となっています。
  • File System Access API にも同様の API サーフェスがありますが、その用途はクライアントのファイル システムとのインターフェースであり、オリジンやブラウザの所有権の範囲外にある可能性のあるデータへのアクセスを提供することです。この異なる焦点には、より厳格なセキュリティ上の考慮事項と、より高いパフォーマンス コストが伴います。
  • IndexedDB API は、Storage Foundation API のユースケースの一部でバックエンドとして使用できます。たとえば、Emscripten には IndexedDB ベースの永続ファイル システムである IDBFS が含まれています。ただし、IndexedDB は基本的にキーと値のストアであるため、パフォーマンスに大きな制限があります。さらに、IndexedDB では、ファイルのサブセクションに直接アクセスすることはさらに困難で、速度も遅くなります。
  • 最後に、CacheStorage インターフェースは広くサポートされており、ウェブ アプリケーション リソースなどの大きなサイズのデータを保存するように調整されていますが、値は変更できません。

Storage Foundation API は、アプリケーションのオリジン内で定義された変更可能な大きなファイルをパフォーマンスの高いストレージに保存できるようにすることで、以前のストレージ オプションのすべてのギャップを埋めようとするものです。

Storage Foundation API のユースケースの例

この API を使用するサイトの例:

  • 大量の動画、音声、画像データを扱う生産性アプリやクリエイティブ アプリ。このようなアプリは、セグメントをメモリに保持するのではなく、ディスクにオフロードできます。
  • Wasm からアクセス可能な永続ファイル システムに依存し、IDBFS が保証できる以上のパフォーマンスを必要とするアプリ。

Storage Foundation API とは何ですか?

API は主に次の 2 つの部分で構成されています。

  • ファイル システム呼び出し。ファイルとファイルパスを操作するための基本的な機能を提供します。
  • 既存のファイルへの読み取り / 書き込みアクセスを提供するファイル ハンドル

ファイル システム呼び出し

Storage Foundation API には、window オブジェクトに存在する新しいオブジェクト storageFoundation が導入されています。このオブジェクトには、次の関数が含まれています。

  • storageFoundation.open(name): 指定された名前のファイルが存在する場合はそのファイルを開き、存在しない場合は新しいファイルを作成します。開いたファイルで解決される Promise を返します。
  • storageFoundation.delete(name): 指定された名前のファイルを削除します。ファイルが削除されたときに解決される Promise を返します。
  • storageFoundation.rename(oldName, newName): ファイルの名前を古い名前から新しい名前にアトミックに変更します。ファイルの名前が変更されたときに解決される Promise を返します。
  • storageFoundation.getAll(): 既存のすべてのファイル名の配列で解決される Promise を返します。
  • storageFoundation.requestCapacity(requestedCapacity): 現在の実行コンテキストで使用する新しい容量(バイト単位)をリクエストします。使用可能な容量の残量で解決された Promise を返します。
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): 現在の実行コンテキストから指定されたバイト数を解放し、残りの容量で解決される Promise を返します。
  • storageFoundation.getRemainingCapacity(): 現在の実行コンテキストで使用可能な容量で解決される Promise を返します。

ファイル ハンドル

ファイル操作は次の関数で行われます。

  • NativeIOFile.close(): ファイルを閉じ、オペレーションが完了したときに解決される Promise を返します。
  • NativeIOFile.flush(): ファイルのメモリ内状態をストレージ デバイスと同期(フラッシュ)し、オペレーションが完了したときに解決される Promise を返します。
  • NativeIOFile.getLength(): ファイルの長さ(バイト単位)で解決される Promise を返します。
  • NativeIOFile.setLength(length): ファイルの長さをバイト単位で設定し、オペレーションが完了したときに解決される Promise を返します。新しい長さが現在の長さより小さい場合、ファイルの末尾からバイトが削除されます。それ以外の場合、ファイルはゼロ値のバイトで拡張されます。
  • NativeIOFile.read(buffer, offset): 指定されたバッファを転送した結果であるバッファを介して、指定されたオフセットにあるファイルの内容を読み取ります。その後、バッファは切り離されたままになります。転送されたバッファと正常に読み取られたバイト数を含む NativeIOReadResult を返します。

    NativeIOReadResult は、次の 2 つのエントリで構成されるオブジェクトです。

    • buffer: ArrayBufferView。これは、read() に渡されたバッファを転送した結果です。ソースバッファと同じ型と長さです。
    • readBytes: buffer に正常に読み取られたバイト数。エラーが発生した場合や、読み取り範囲がファイルの末尾を超える場合は、バッファサイズよりも小さくなることがあります。読み取り範囲がファイルの末尾を超えている場合は、ゼロに設定されます。
  • NativeIOFile.write(buffer, offset): 指定されたバッファの内容を指定されたオフセットのファイルに書き込みます。バッファはデータが書き込まれる前に転送されるため、切り離されたままになります。転送されたバッファと正常に書き込まれたバイト数を含む NativeIOWriteResult を返します。書き込み範囲が長さを超える場合、ファイルは拡張されます。

    NativeIOWriteResult は、次の 2 つのエントリで構成されるオブジェクトです。

    • buffer: write() に渡されたバッファの転送結果である ArrayBufferView。ソースバッファと同じ型と長さです。
    • writtenBytes: buffer に正常に書き込まれたバイト数。エラーが発生した場合、バッファサイズよりも小さくなることがあります。

完全な例

上記のコンセプトをより明確にするために、Storage Foundation ファイルのライフサイクルのさまざまなステージを説明する 2 つの完全な例を次に示します。

開く、書き込む、読み取る、閉じる

// Open a file (creating it if needed).
const file = await storageFoundation.open('test_file');
try {
  // Request 100 bytes of capacity for this context.
  await storageFoundation.requestCapacity(100);

  const writeBuffer = new Uint8Array([64, 65, 66]);
  // Write the buffer at offset 0. After this operation, `result.buffer`
  // contains the transferred buffer and `result.writtenBytes` is 3,
  // the number of bytes written. `writeBuffer` is left detached.
  let result = await file.write(writeBuffer, 0);

  const readBuffer = new Uint8Array(3);
  // Read at offset 1. `result.buffer` contains the transferred buffer,
  // `result.readBytes` is 2, the number of bytes read. `readBuffer` is left
  // detached.
  result = await file.read(readBuffer, 1);
  // `Uint8Array(3) [65, 66, 0]`
  console.log(result.buffer);
} finally {
  file.close();
}

開く、一覧表示する、削除する

// Open three different files (creating them if needed).
await storageFoundation.open('sunrise');
await storageFoundation.open('noon');
await storageFoundation.open('sunset');
// List all existing files.
// `["sunset", "sunrise", "noon"]`
await storageFoundation.getAll();
// Delete one of the three files.
await storageFoundation.delete('noon');
// List all remaining existing files.
// `["sunrise", "noon"]`
await storageFoundation.getAll();

セキュリティと権限

Chromium チームは、強力なウェブ プラットフォーム機能へのアクセスを制御するで定義されたユーザー制御、透明性、人間工学などの基本原則を使用して、Storage Foundation API を設計、実装しました。

ウェブ上の他の最新のストレージ API と同様のパターンに従い、Storage Foundation API へのアクセスはオリジンにバインドされます。つまり、オリジンは自身で作成したデータにのみアクセスできます。また、安全なコンテキストに限定されます。

ユーザー コントロール

ストレージ割り当ては、ディスク容量へのアクセスを分散し、不正使用を防ぐために使用されます。占有するメモリは、最初にリクエストする必要があります。他のストレージ API と同様に、ユーザーはブラウザから Storage Foundation API が使用するスペースをクリアできます。

関連情報

謝辞

Storage Foundation API は、Emanuel KrivoyRichard Stotz によって指定され、実装されました。この記事は Pete LePageJoe Medley によってレビューされました。

ヒーロー画像: Markus SpiskeUnsplash