このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。

View in English Always switch to English

FileSystemFileHandle: createSyncAccessHandle() メソッド

Baseline
広く利用可能

この機能は広く実装されており、多くのバージョンの端末やブラウザーで動作します。2023年3月以降、すべてのブラウザーで利用可能です。

安全なコンテキスト用: この機能は一部またはすべての対応しているブラウザーにおいて、安全なコンテキスト (HTTPS) でのみ利用できます。

メモ: この機能は専用ウェブワーカー内でのみ利用可能です。

createSyncAccessHandle()FileSystemFileHandle インターフェイスのメソッドで、Promise を返します。このプロミスは、ファイルへの同期的な読み取りおよび書き込みに使用できる FileSystemSyncAccessHandle オブジェクトに解決されます。 このメソッドは同期型であるためパフォーマンス上の利点がありますが、オリジンプライベートファイルシステム内のファイルに対しては、専用ウェブワーカー内でのみ使用可能です。

FileSystemSyncAccessHandle を作成すると、ファイルハンドルに対応するファイルの排他的ロックを取得します。これにより、作成したアクセスハンドルを閉じるまで、同じファイルについて FileSystemSyncAccessHandleFileSystemWritableFileStream を作成することはできなくなります。

構文

js
createSyncAccessHandle()
createSyncAccessHandle(options)

引数

options 省略可

以下のプロパティを持つオブジェクトです。

mode 省略可

アクセスハンドルのロックモードを指定する文字列。デフォルト値は "readwrite" です。 指定可能な値は次の通りです。

"read-only"

1 つのファイルに対して、複数の FileSystemSyncAccessHandle オブジェクトを同時に開くことができます(例えば、同じアプリを複数のタブで開いている場合など)。ただし、それらはすべて "read-only" モードで開かれている必要があります。一度開かれると、ハンドルに対して読み取り系のメソッド、read()getSize()close() を呼び出すことができます。

"readwrite"

1 つのファイルに対して開くことができる FileSystemSyncAccessHandle オブジェクトは 1 つだけです。最初のハンドルが閉じられる前にもう 1 つのハンドルを開こうとすると、NoModificationAllowedError 例外が発生します。一度開けば、そのハンドルに対して利用できる任意のメソッドを呼び出すことができます。

"readwrite-unsafe"

1 つのファイルに対して、複数の FileSystemSyncAccessHandle オブジェクトを同時に開くことができますが、すべて "readwrite-unsafe" モードで開かれます。一度開かれると、それらのハンドルに対して利用できる任意のメソッドを呼び出すことができます。

返値

FileSystemSyncAccessHandle オブジェクトで解決される Promise です。

例外

NotAllowedError DOMException

readwrite モードでハンドルの PermissionStatus.stategranted でない場合に発生します。

InvalidStateError DOMException

FileSystemSyncAccessHandle オブジェクトがオリジンプライベートファイルシステム内のファイルを表していないとき発生します。

NotFoundError DOMException

現在の項目が見つからなかった場合に発生します。

NoModificationAllowedError DOMException

ブラウザーがファイルハンドルに関連付けられたファイルのロックを取得できない場合に発生します。これは、modereadwrite に設定されており、複数のハンドルを同時に開こうとしたことが原因である可能性があります。

基本的な使い方

以下の非同期のイベントハンドラーは、ウェブワーカー内にあります。そのうちのこの部分は、同期ファイルアクセスハンドルを作成します。

js
onmessage = async (e) => {
  // メインスクリプトから送られた処理対象のメッセージを取得する
  const message = e.data;

  // draft ファイルへのハンドルを取得する
  const root = await navigator.storage.getDirectory();
  const draftHandle = await root.getFileHandle("draft.txt", { create: true });
  // 同期式アクセスハンドルを取得する
  const accessHandle = await draftHandle.createSyncAccessHandle();

  // …

  // 完了したら、常に FileSystemSyncAccessHandle を閉じる
  accessHandle.close();
};

mode オプションの完全な例

createSyncAccessHandle() モードのテスト の例(ソースコードを参照)では、テキストを入力するための<input>フィールドと、2 つのボタンが用意されています。1 つは入力されたテキストをオリジンプライベートファイルシステム内のファイルの末尾に書き込むためのもの、もう 1 つはファイルが満杯になった際にその内容を空にするためのものです。

上記のデモを試してみて、何が起きているかを確認できるように、ブラウザーの開発者コンソールを開いておいてください。デモを複数のブラウザータブで開いてみると、複数のハンドルを同時に開き、ファイルへの書き込みを並行して行えることがわかります。これは、createSyncAccessHandle() の呼び出しで mode: "readwrite-unsafe" が設定されているためです。

下記では、そのコードについて詳しく見ていきます。

HTML

2 つの <button> 要素とテキストの <input> フィールドが次のようにあります。

html
<ol>
  <li>
    <label for="file-text">ファイルに書き込むテキストを入力してください:</label>
    <input type="text" id="file-text" name="file-text" />
  </li>
  <li>
    テキストをファイルへ書き込む: <button class="write">テキストを書き込む</button>
  </li>
  <li>
    ファイルがいっぱいになったら、中身を空にする:
    <button class="empty">ファイルを空にする</button>
  </li>
</ol>

メイン JavaScript

HTMLファイル内のメインスレッドのJavaScriptは下記です。まず、「テキストを書き込む」ボタン、「ファイルを空にする」ボタン、テキスト入力フィールドへの参照を取得し、Worker() コンストラクターを使用して新しいウェブワーカーを生成します。次に、2 つの関数を定義し、それらをボタンのイベントハンドラーとして設定します。

  • writeToOPFS() は、「テキストを書き込む」ボタンがクリックされたときに実行されます。この関数は、Worker.postMessage() メソッドを使用して、テキストフィールドに入力された値をオブジェクト内に格納し、それをワーカーに送信します。その後、テキストフィールドの内容をクリアして、次回の追加に備えます。渡されるオブジェクトには、このメッセージで書き込みアクションを起動するよう指定するための command: "write" プロパティも指定されている点に注意してください。
  • emptyOPFS() は、「ファイルを空にする」ボタンがクリックされたときに実行されます。この関数は、command: "empty" プロパティを含むオブジェクトをワーカーに送信し、ファイルを空にするよう指定します。
js
const writeBtn = document.querySelector(".write");
const emptyBtn = document.querySelector(".empty");
const fileText = document.querySelector("#file-text");

const opfsWorker = new Worker("worker.js");

function writeToOPFS() {
  opfsWorker.postMessage({
    command: "write",
    content: fileText.value,
  });
  console.log("メインスクリプト: テキストがワーカーへ渡されました");
  fileText.value = "";
}

function emptyOPFS() {
  opfsWorker.postMessage({
    command: "empty",
  });
}

writeBtn.addEventListener("click", writeToOPFS);
emptyBtn.addEventListener("click", emptyOPFS);

ワーカー JavaScript

ワーカーの JavaScript を下記に示します。

まず、initOPFS() という関数を実行します。この関数は、StorageManager.getDirectory() を使用して OPFS ルートの参照を取得し、ファイルを作成して FileSystemDirectoryHandle.getFileHandle() を使用してそのハンドルを返し、 その後、createSyncAccessHandle() を使用して FileSystemSyncAccessHandle を返します。この呼び出しには mode: "readwrite-unsafe" プロパティが記載されており、これにより複数のハンドルが同時に同じファイルにアクセスすることができます。

js
let accessHandle;

async function initOPFS() {
  const opfsRoot = await navigator.storage.getDirectory();
  const fileHandle = await opfsRoot.getFileHandle("file.txt", { create: true });
  accessHandle = await fileHandle.createSyncAccessHandle({
    mode: "readwrite-unsafe",
  });
}

initOPFS();

ワーカーの message イベントハンドラー関数内では、まず getSize() を使用してファイルのサイズを取得します。次に、メッセージで送信されたデータに "empty" という値を持つ command プロパティが含まれているかどうかを調べます。含まれている場合は、値を 0 として truncate() を使用してファイルを空にし、size 変数に格納されているファイルサイズを更新します。

メッセージデータがそれ以外の場合は、次のようにします。

  • 後でテキストコンテンツのエンコード方式とデコードを処理するために、新しい TextEncoderTextDecoder を作成します。
  • メッセージデータをエンコードし、write() を使用して結果をファイルの末尾に書き込んだ後、size 変数に格納されているファイルサイズを更新します。
  • ファイルの内容を格納する DataView を作成し、read() を使用してその内容を読み込みます。
  • DataView のコンテンツをデコードし、コンソールにログ出力します。
js
onmessage = function (e) {
  console.log("ワーカー: メッセージをメインスクリプトから取得");

  // ファイルの現在のサイズを取得
  let size = accessHandle.getSize();

  if (e.data.command === "empty") {
    // ファイルを 0 バイトに切り詰める
    accessHandle.truncate(0);

    // ファイルの現在のサイズを取得
    size = accessHandle.getSize();
  } else {
    const textEncoder = new TextEncoder();
    const textDecoder = new TextDecoder();

    // ファイルに書き込むコンテンツをエンコード
    const content = textEncoder.encode(e.data.content);
    // ファイルの末尾にコンテンツを書き込む
    accessHandle.write(content, { at: size });

    // ファイルの現在のサイズを取得
    size = accessHandle.getSize();

    // ファイルの長さを示すデータビューを準備
    const dataView = new DataView(new ArrayBuffer(size));

    // ファイル全体をデータビューに取り込む
    accessHandle.read(dataView, { at: 0 });

    // 現在のファイルの内容をコンソールにログ出力
    console.log(`File contents: ${textDecoder.decode(dataView)}`);

    // 変更を反映
    accessHandle.flush();
  }

  // ファイルサイズをコンソールにログ出力
  console.log(`Size: ${size}`);
};

仕様書

仕様書
File System
# api-filesystemfilehandle-createsyncaccesshandle

ブラウザーの互換性

関連情報