コンテンツにスキップ
UniKVS
日本語
Esc
↑↓移動↵開く⌘Jプレビュー
このページの内容

@unikvs/fs.node

Node.js のローカルファイルシステムにバイト列を保存するストレージプラグインの使い方を説明します。

概要

@unikvs/fs.node は、Node.js のローカルファイルシステムに保存するストレージプラグインです。指定したルートディレクトリー配下にキーをファイル名として保存します。Node.js 専用

扱うデータはバイト列専用です。write は Uint8Array<ArrayBuffer> を受け付け、read は Uint8Array<ArrayBuffer> を返します。実装は node:fs、node:path、node:crypto、node:stream を動的インポートして使います。

インストールは次のように行います。

npm install @unikvs/fs.node
pnpm add @unikvs/fs.node
yarn add @unikvs/fs.node
bun add @unikvs/fs.node
nub add @unikvs/fs.node
aube add @unikvs/fs.node

依存関係として @unikvs/core と @unikvs/utils を使用します。

使い方

中心は NodeFs クラスです。

import { NodeFs } from "@unikvs/fs.node";
public constructor(root: string = ".unikvs", options: NodeFsOptions = {})

root は保存先のルートディレクトリーです。相対パスは open() 実行時にカレントディレクトリー基準で解決します。既定値は ".unikvs" です。

options.allowRepair は書き戻しによる書き込みを許可するかどうかです。既定値は false です。false を指定すると、書き戻し(vars["unikvs:repair"] が true)の write・getWritable は RepairNotAllowedError で失敗します。

主なメンバーは次のとおりです。

  • name は "NodeFs" です。
  • allowRepair は書き戻しによる書き込みを許可するかどうかを示します。
  • isOpen はオープン済みかどうかを示します。
  • open() はルートディレクトリーを再帰的に作り、読み書き可能にします。
  • write・read・exists・delete・clear で読み書きします。
import { NodeFs } from "@unikvs/fs.node";

const storage = new NodeFs("./.tmp/unikvs-fs");
await storage.open();

await storage.write({
  key: "hello.bin",
  data: new TextEncoder().encode("hello"),
});

const data = await storage.read({ key: "hello.bin" });
console.log(new TextDecoder().decode(data));

ファイル配置

キーからファイルパスへの対応は次のとおりです。ルート直下にキーがそのまま並び、サブディレクトリーは作りません。

  • .unikvs/
    • hello.bin
    • greeting.bin
  • 最終パスは path.join(this.root, key) です。
  • 書き込み時の一時ファイルは `${dest}.${crypto.randomUUID()}.tmp` です。同じディレクトリーに作り、rename がアトミックになることを保証します。
  • open() では path.resolve(this.root) で絶対パス化し、mkdir(root, { recursive: true }) で再帰的に作ります。
  • clear() ではルートを rm(this.root, { recursive: true, force: true }) で削除後に作り直します。

ファイル名の検証には @unikvs/utils の assertValidFilename を使います。write・read・exists・delete・getWritable・getReadable のいずれも最初に検証し、無効なら InvalidFilenameError を投げます。空文字、.、..、255 バイト超、NFC 不一致、予約文字・Windows 予約名を含むキーは使えません。

ストリーム

読み書きどちらのストリームにも対応しています。

書き込み用は次のメソッドで取得します。

public async getWritable(
  args: Pick<IStorage.GetWritableArgs, "key"> & { signal?: AbortSignal },
): Promise<WritableStream<Uint8Array<ArrayBuffer>>>

一時ファイルへ書き出し、close 成功時のみ最終パスへ rename する swap-on-close 方式です。中断時はストリームを破棄して一時ファイルを削除します。バックプレッシャーに対応し、失敗・中断時に既存データを壊しません。

読み取り用は次のメソッドで取得します。

public getReadable(
  args: Pick<IStorage.GetReadableArgs, "key" | "signal">,
): ReadableStream<Uint8Array<ArrayBuffer>>

fs.createReadStream(file, { signal }) を stream.Readable.toWeb(readStream) で Web ストリームに変換して返します。signal を中断すると読み取りストリームが中断理由で破棄され、進行中の読み取りも失敗します。

注意点

  • open() 前に write・read を実行しないでください。内部の接続が null のため処理できません。isOpen で確認できます。
  • open() はオープン済みでも再初期化します。root は絶対パスで保持します。
  • write は一時ファイルへの書き込み後に rename します。失敗・中断時は一時ファイルを削除して既存データを保全します。
  • 同一キーへの並行書き込みは一時ファイルが衝突しません。最後に rename が完了した書き込みが残ります。
  • exists は access(file) の成否で判定し、エラー時は false を返します。
  • delete は存在しないファイルだとエラーになります。
  • 権限や所有者は変更しません。OS と Node.js の既定値に従います。
  • close() はありません。IStorage の close は任意のため、後始末が必要なら各自で管理してください。

使用例

.tmp/ 配下に保存する最小完動例です。

import { NodeFs } from "@unikvs/fs.node";

const storage = new NodeFs("./.tmp/unikvs-fs-node-example");
await storage.open();

const key = "greeting.bin";
await storage.write({
  key,
  data: new TextEncoder().encode("hello, NodeFs"),
});

if (await storage.exists({ key })) {
  const data = await storage.read({ key });
  console.log(new TextDecoder().decode(data));
}

await storage.delete({ key });
await storage.clear();

このページは役に立ちましたか?