はじめに

OPFS は独自の仕組みのように見えますが、実際には File System Access API の考え方を利用しています。

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

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


File System Access API の基本構造

~ FileSystemDirectoryHandle と FileSystemFileHandle を理解しよう~

まずは、下記のディレクトリ構成のイメージを思い浮かべてください。

ルート
 └─ images
       ├─ image001.jpg
       ├─ image002.jpg
       └─ image003.jpg

ルートディレクトリの配下に images というディレクトリがあり、その配下に、 image001.jpg 、 image002.jpg 、 image003.jpg の 3 つの画像ファイルが格納されています。

ここで、 image002.jpg ファイルを読み込みたいとします。

手順としては、

  1. ルートディレクトリの配下に登録されている images ディレクトリを取得します。
  2. images ディレクトリ配下に登録されている image002.jpg ファイルを取得します。
  3. image002.jpg ファイルを読み込みます。

このように、ディレクトリ構成のファイルにアクセスする為には、先頭から順番にその枝葉を伝うように、該当のファイルを探し当てる必要があります。

File System Access API は、この為の方法を提供してくれます。

では、どのようにして、提供してくれるのか。

File System Access API では、上記の root 、 images 、 image001.jpg 、 image002.jpg 、 image003.jpg といった部分をアクセスする為に、抽象化した Handle オブジェクト が用意されています。

Handle(ハンドル)とは、ファイルやディレクトリそのものではなく、それらへアクセスするための「窓口」や「参照情報」のようなものです。

そして、Handle オブジェクトには、

  • root 、 images といったディレクトリを示す FileSystemDirectoryHandle オブジェクト
  • image001.jpg 、 image002.jpg 、 image003.jpg といったファイルを示す FileSystemFileHandle オブジェクト

の 2 種類が存在します。

また、実際に、

  • ファイル読み込み用として、 File オブジェクト

※ File オブジェクトは File System Access API 独自のものではなく、input type="file" などでも利用される Web標準の File オブジェクトです。

  • ファイルへの書き込み用として、FileSystemWritableFileStream オブジェクト
  • ファイルへの同期アクセス用として、FileSystemSyncAccessHandle オブジェクト

が存在します。これらは、FileSystemFileHandle オブジェクト のメソッドを通して取得できます。

つまり、File System Access API では、下記の様なオブジェクトのイメージとなっています。

ルート FileSystemDirectoryHandle
 └─ images FileSystemDirectoryHandle
       ├─ image001.jpg FileSystemFileHandle
       │     ├─ File                            ・・・ 読み込み時使用
       │     ├─ FileSystemWritableFileStream    ・・・ 書き込み時使用
       │     └─ FileSystemSyncAccessHandle      ・・・ 同期アクセス時使用
       ├─ image002.jpg FileSystemFileHandle
       │     ├─ File                            ・・・ 読み込み時使用
       │     ├─ FileSystemWritableFileStream    ・・・ 書き込み時使用
       │     └─ FileSystemSyncAccessHandle      ・・・ 同期アクセス時使用
       └─ image003.jpg FileSystemFileHandle
              ├─ File                            ・・・ 読み込み時使用
              ├─ FileSystemWritableFileStream    ・・・ 書き込み時使用
              └─ FileSystemSyncAccessHandle      ・・・ 同期アクセス時使用

先ほどの、image002.jpg ファイルの読み込みを File System Access API で表現すると下記のようになります。

  1. root FileSystemDirectoryHandle 配下の images FileSystemDirectoryHandle を取得する。
  2. images FileSystemDirectoryHandle 配下の image002.jpg FileSystemFileHandle を取得する。
  3. image002.jpg FileSystemFileHandle から File を取得し、image002.jpg ファイルを読み込みます。

このように、File System Access API では、抽象化されたオブジェクト群を順番にたどることで、ディレクトリ構成のファイルシステムにアクセスが可能となっています。


主なAPI解説

navigator.storage.getDirectory()

OPFSのルートディレクトリの FileSystemDirectoryHandle オブジェクトを取得するメソッドです。

OPFS へのアクセスには、まずこれが必要です。

const root = await navigator.storage.getDirectory();


FileSystemDirectoryHandle オブジェクト

ディレクトリを操作する為のオブジェクトです。

主なメソッド

getDirectoryHandle()
getFileHandle()
removeEntry()
entries()
keys()
values()

使用例

const imagesHandle = await root.getDirectoryHandle("images", { create: true });


FileSystemFileHandle オブジェクト

ファイルを操作するオブジェクトです。

主なメソッド

getFile()
createWritable()
createSyncAccessHandle()
remove()

使用例

const fileHandle = await imagesHandle.getFileHandle("image002.jpg", { create: false });


File オブジェクト

ファイル内容を取得するオブジェクトです。

主なメソッド

text()
arrayBuffer()
stream()

使用例

const file = await fileHandle.getFile();
content = await file.arrayBuffer();
return new Uint8Array(content); // バイナリデータを返す


FileSystemWritableFileStream オブジェクト

ファイルへ書き込みを行うオブジェクトです。

主なメソッド

write()
seek()
truncate()
close()

使用例

const writable = await fileHandle.createWritable();
await writable.write({ type: "write", position: 0, data: encodedData });
await writable.close();

※ 書き込み後は close() を呼び出して確定する必要があります。


FileSystemSyncAccessHandle オブジェクト

ファイルへ同期アクセスを行うオブジェクトです。

※ 本オブジェクトを使った操作は、ブラウザのイベントループ処理では、使用できません。使用には、 Web Worker が必要です。

主なメソッド

read()
write()
truncate()
getsize()
flush()
close()

使用例

const accessHandle = await fileHandle.createSyncAccessHandle();
const encodedData = new TextEncoder().encode(data);
accessHandle.write(encodedData, { at: 0 });
accessHandle.close();

※ 書き込み後は flush() もしくは、close() を呼び出して確定する必要があります。


File System Access API によるコード例

先ほどの、image002.jpg ファイルの読み込みを File System Access API で記述すると下記の様になります。

// OPFS のルートディレクトリを取得します。
const root = await navigator.storage.getDirectory();

// 1. ルートディレクトリの配下に登録されている images ディレクトリを取得します。
//    (root FileSystemDirectoryHandle 配下の images FileSystemDirectoryHandle を取得する。)
const imagesHandle = await root.getDirectoryHandle("images", { create: false });

// 2. images ディレクトリ配下に登録されている image002.jpg ファイルを取得します。
//    (images FileSystemDirectoryHandle 配下の image002.jpg FileSystemFileHandle を取得する。)
const fileHandle = await imagesHandle.getFileHandle("image002.jpg", { create: false });

// 3. image002.jpg ファイルを読み込みます。
//    (image002.jpg FileSystemFileHandle から File を取得し、image002.jpg ファイルを読み込みます。)
const file = await fileHandle.getFile();
content = await file.arrayBuffer();
return new Uint8Array(content); // バイナリデータを返す


まとめ

  • OPFS には、File System Access API で、抽象化されたオブジェクト群を順番にたどることで、アクセス可能です。

全体イメージ図

                navigator.storage
                        │
                        ▼
                  getDirectory()
                        │
                        ▼
           FileSystemDirectoryHandle
                        │
          ┌─────────────┴──────────────┐
          ▼                            ▼
getDirectoryHandle()            getFileHandle()
          │                            │
          ▼                            ▼
FileSystemDirectoryHandle     FileSystemFileHandle
                                       │
           ┌───────────────────────────┼────────────────────────────┐
           ▼                           ▼                            ▼
       getFile()                createWritable()         createSyncAccessHandle()
           │                           │                            │
           ▼                           ▼                            ▼
         File           FileSystemWritableFileStream    FileSystemSyncAccessHandle
       (読み込み)              (非同期書き込み)                  (同期書き込み)