@unikvs/opfs
ブラウザーの OPFS にバイト列を保存するストレージプラグインの使い方を説明します。
概要
@unikvs/opfs は、ブラウザーの OPFS(Origin Private File System)に保存するストレージプラグインです。データはバイト列(Uint8Array<ArrayBuffer>)専用です。ブラウザー専用
メインスレッド・Web Worker での動作を前提としています。
インストールは次のように行います。
npm install @unikvs/opfspnpm add @unikvs/opfsyarn add @unikvs/opfsbun add @unikvs/opfsnub add @unikvs/opfsaube add @unikvs/opfs使い方
クラス名は Opfs です。デフォルト・名前付きのどちらでも取得できます。
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
コンストラクター引数 root の型は string | FileSystemDirectoryHandle で、既定値は ".unikvs" です。文字列は OPFS 内のルート名、ハンドルは作業対象そのものとして扱います。ハンドルを渡すとオープン済み扱いです。第 2 引数 options は省略可能で、options.allowRepair(boolean、既定値 false)で書き戻しによる書き込みの許可を指定します。false の場合、書き戻し(vars["unikvs:repair"] が true)の write・getWritable はエラーを投げます。
使う前に open() を呼び出します。UniKvs と組み合わせる場合は、appendStorage で登録してから kvs.open() を呼びます。内部で Opfs.open() が呼ばれます。
import { UniKvs } from "unikvs";
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
const kvs = UniKvs.config().appendStorage(storage).create();
await kvs.open();
// バイト列の読み書きを行います。
await kvs.close();
直接操作する場合は、write・read・exists・delete・clear を使います。いずれも事前の open() が必要です。
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
await storage.open();
await storage.write({ key: "data.bin", data: new Uint8Array([1, 2, 3]) });
const data = await storage.read({ key: "data.bin" });
const found = await storage.exists({ key: "data.bin" });
await storage.delete({ key: "data.bin" });
ファイル配置
キーはファイル名として、指定ルートの直下に 1 対 1 で保存します。サブディレクトリーは作りません。キーは assertValidFilename で検証し、無効ならエラーになります。
文字列ルートは次の規則で正規化します。
""・"."・"/"は空文字扱いで、OPFS ルート直下が作業対象になります。- 連続する
/は 1 つにまとめ、先頭と末尾の/は取り除きます。 /で分割した各階層はassertValidDirnameで検証します。
open() では navigator.storage.getDirectory() で起点を取得し、各階層を create: true で作成・取得します。clear() では、ルート直下なら各エントリーを個別に削除し、サブディレクトリーならごと削除して作り直します。
ストリーム
書き込み・読み取りの両ストリームに対応しています。
getWritable({ key, signal })はWritableStream<Uint8Array<ArrayBuffer>>を返します。内部ではファイルハンドルからcreateWritable()で取得したストリームをライター経由で包みます。signalを中断すると書き込み途中の変更を破棄します。getReadable({ key, signal })はReadableStream<Uint8Array<ArrayBuffer>>を返します。内部ではファイルを取得し、File.stream()をリーダー経由で包みます。signalを中断すると読み取りを中止し、以降の読み取りは中断理由で失敗します。
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
await storage.open();
const writable = await storage.getWritable({ key: "large.bin" });
const writer = writable.getWriter();
await writer.write(new Uint8Array([1, 2, 3]));
await writer.close();
const readable = await storage.getReadable({ key: "large.bin" });
await readable.cancel();
注意点
open()前にread・writeを行うと実行時エラーになります。必ず先にオープンしてください。- すべてのメソッドは
signalを受け取ります。各操作は待機の区切りでsignalを確認し、中断されていればその中断理由で失敗します。openが中断された場合は未オープンのままです。 writeは失敗時にabortで変更を破棄するため、部分書き込みで既存データを壊しません。existsは存在しないキーにfalseを返します。NotFoundErrorのDOMExceptionを捕捉して判定し、それ以外は投げます。- キーとルート名にはファイル名の制約があります。無効な名前は検証エラーになります。
使用例
ブラウザーで Opfs を直接使用する最小例を示します。
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
await storage.open();
const key = "hello.bin";
await storage.write({ key, data: new TextEncoder().encode("hello") });
const loaded = await storage.read({ key });
console.log(new TextDecoder().decode(loaded));
console.log(await storage.exists({ key }));
await storage.delete({ key });