はじめに

ブラウザでデータを保存する方法として、LocalStorage や IndexedDB をご存じの方は多いと思います。

当カテゴリーでも、それらについて詳しく取り上げ、実際の動きを確認してきました。

特に IndexedDB は、JavaScript から利用できる高機能なデータベースとして広く利用されています。

キー検索やインデックス機能を備えており、構造化データを効率的に管理できるからです。

ただ、近年の Web アプリケーションは単なる Web サイトではなく、デスクトップアプリに近い機能を持つようになってきております。

それにともない、Webブラウザ上でもファイル単位でデータを管理したい場面が増えてきました。

例えば、

  • 画像編集アプリ
  • 動画編集アプリ
  • CADツール
  • オフライン対応アプリ

などでは、大量のバイナリデータやファイルを扱う必要があります。

しかし従来のブラウザストレージは、主に「データベース」として設計されており、「ファイルシステム」として利用するには不便な面がありました。

そこで、ブラウザ上でもファイルシステムのような操作を実現する仕組みとして登場したのが OPFS(Origin Private File System) です。

ただし、OPFS は IndexedDB の代替ではありません。

両者にはそれぞれ得意な用途があり、実際のアプリケーションでは組み合わせて利用されることも少なくありません。

この記事では、OPFS の特徴や IndexedDB との違い、基本的な使い方について解説します。


OPFSとは?

OPFS(Origin Private File System)は、ブラウザが提供するアプリ専用のファイルシステムです。

簡単に言えば、「ブラウザ内に作られる専用フォルダ」のようなものです。

通常のファイルシステムと同じように、

  • ファイル作成
  • ファイル削除
  • ディレクトリ作成
  • データの読み書き

が行えます。

👉 ただし、ユーザーがエクスプローラーやFinderから直接見ることはできません。

ブラウザとWebアプリだけがアクセスできる専用領域として管理されています。

イメージ

OPFS 上に、config.json, image.jpg, database.sqlite の 3 つのファイルが管理されている例:

ブラウザ
 └─ Webアプリ
      └─ OPFS
           ├─ config.json
           ├─ image.jpg
           └─ database.sqlite


なぜOPFSが必要なのか

従来からブラウザには IndexedDB が存在します。

しかし、 IndexedDB は「オブジェクトを保存するためのデータベース」です。

例えば次のようなデータを保存するのは得意です。

{
  id: 1,
  title: "記事タイトル",
  createdAt: "2025-01-01"
}

一方で、

  • 巨大な画像ファイル
  • 動画ファイル
  • SQLiteデータベースファイル

などを扱うことも可能ですが、ファイルとして管理する用途には必ずしも向いていません。

👉 そこで、 「データベースではなくファイルとして管理したい」 というニーズに応えるために OPFS が登場しました。


IndexedDBとの違い

IndexedDB はオブジェクトストアを持つデータベースです。

Database
 └─ ObjectStore
      └─ Object

👉 検索機能やインデックス機能があり、構造化データの保存が得意です。

一方、 OPFS はファイルシステムです。

Directory
 ├─ image.png
 ├─ video.mp4
 └─ data.sqlite

👉 ディレクトリ構造を持ち、ファイル単位でデータを管理します。

比較表

項目 IndexedDB OPFS
保存形式 オブジェクト ファイル
検索機能 ×
バイナリデータ
巨大ファイル
SQLiteとの相性
データ管理 DB形式 ファイル形式


どちらを使うべきか

IndexedDB向き

  • 設定情報
  • ユーザー情報
  • 商品一覧
  • ToDoデータ

OPFS向き

  • 画像
  • 動画
  • 音声
  • SQLiteデータベース
  • AIモデル

👉 このように、両者は競合技術ではなく、用途によって使い分けるものです。


OPFSのメリット

  • ファイルシステムとして扱える

Node.jsのfsモジュールに近い感覚で利用できます。

  • 大容量データに強い

画像や動画などの大きなファイルを効率的に扱えます。

  • ランダムアクセスが可能

ファイルの途中だけを書き換えられます。

await writable.seek(1024);
await writable.write(data);
  • SQLiteとの相性が良い

近年 SQLite WASM の保存先として OPFS が利用できるようになりました。本格的な SQL データベースが利用できることで、 ブラウザ上でありながら、ネイティブアプリに近い性能が期待できるようになっています。


OPFSのデメリット

  • 検索機能がない

IndexedDBのようなインデックス機能はありません。

  • データ管理を自分で行う必要がある

ファイル名管理やディレクトリ構造の設計が必要です。

  • 比較的新しいAPIのため、古いブラウザでは利用できない

ブラウザによって対応状況に差があるため、実運用前には対象ブラウザで動作確認を行うことをおすすめします。


基本的な使い方

OPFS はブラウザ独自の API ではなく、File System Access API の仕組みを利用しています。

File System Access API は、JavaScriptからファイルやディレクトリを扱うためのAPI群です。

そのため、FileSystemDirectoryHandle や FileSystemFileHandle など、多くのオブジェクトは、ユーザーのファイルを扱う場合と共通のインターフェースになっています。

// ルートディレクトリの取得
const root = await navigator.storage.getDirectory();

// ファイル作成
const handle = await root.getFileHandle("sample.txt", { create: true });

// ファイル書き込み
const writable = await handle.createWritable();
await writable.write("Hello OPFS");
await writable.close();

// ファイル読み込み
const file = await handle.getFile();
const text = await file.text();
console.log(text);

// ファイル削除
await root.removeEntry("sample.txt");


使用上の制約

OPFS は非常に便利な仕組みですが、利用する際にはいくつか知っておきたい制約があります。

特に、初めて利用する場合は、

  • なぜ動かないのか
  • なぜエラーになるのか

で悩むことも少なくありません。

ここでは、事前に知っておきたい主な制約について解説します。

オリジンごとに管理される

OPFS のオリジンは、Web サイトのオリジン(スキーム・ホスト・ポートの組み合わせ)を意味します。

例えば、

https://example.com

https://sub.example.com

は別のオリジンとして扱われます。

また、

https://example.com

http://example.com

も別のオリジンです。

👉 そのため、あるオリジンで保存したデータを、別のオリジンから参照することはできません。

全くの別領域として管理されます。

HTTPS環境が必要

OPFS はセキュアコンテキストでのみ利用できます。

通常は HTTPS 環境であることが条件です。

例えば、

https://example.com

では利用できますが、

http://example.com

では利用できません。

ただし、開発用途として、

http://localhost

は例外的に許可されています。

👉 そのため、ローカル開発環境であれば HTTPS 化していなくても動作確認を行えます。

ユーザーは直接アクセスできない

OPFS はブラウザ内部の保存領域です。

保存されたファイルは、通常のファイルシステムのようにエクスプローラーや Finder から参照できません。

例えば、

image.jpg
config.json
database.sqlite

といったファイルを保存しても、ユーザーが直接開くことはできません。

あくまで Web アプリ専用の保存領域として利用されます。

ブラウザごとに容量制限がある

OPFS はローカルディスクを利用しますが、無制限に保存できるわけではありません。

利用可能な容量は、

  • ブラウザ
  • OS
  • 空きディスク容量

などによって変化します。

大量のデータを扱うアプリケーションでは、利用可能なストレージ容量を事前に確認することをおすすめします。

例えば、Storage API を利用すると概算値を取得できます。

const estimate = await navigator.storage.estimate();

console.log(estimate.quota);
console.log(estimate.usage);

Sync Access Handle 利用時は追加設定が必要

通常の OPFS API は、HTTPS 環境であればそのまま利用できます。

しかし、高速なファイルアクセスを実現する

createSyncAccessHandle()

を利用する場合は注意が必要です。

利用するブラウザや実行環境によっては、 Cross-Origin Isolation が要求されます。

ちなみに、 Cross-Origin Isolation(クロスオリジン分離)は、他のオリジンから読み込んだコンテンツと、自分のページを厳密に分離するためのセキュリティ機能のことです。

その場合、サーバーから以下のHTTPヘッダーを返す必要があります。

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

この設定は、

  • SQLite WASM
  • 高速なデータベース処理
  • 大量データ処理

などで利用されるケースがあります。

基本的なファイル操作だけであれば不要ですが、実践的なアプリ開発では遭遇することの多い制約です。

(つづく)