ファイルシステム

現行標準 — 最終更新

参加方法:
GitHub whatwg/fs (新規issue, オープンissue)
Matrixでチャット
コミット:
GitHub whatwg/fs/commits
このコミット時点でのスナップショット
テスト:
web-platform-tests fs/ (進行中の作業)
翻訳 (非規範的):
日本語
简体中文
한국어

概要

ファイルシステムは、ファイルシステムのインフラとそのAPIを定義します。

1. 序章

このセクションは規範的ではありません。

この文書はファイルシステムAPIのための基盤的なインフラを定義します。また、事前にユーザーにアクセスを求めるプロンプトを表示することなく、ウェブサイトがファイルシステムディレクトリにアクセスできるAPIも定義します。これにより、ユーザーが保存先を選択する前にウェブサイトがデータをディスクに保存したい場合に、完全に異なるストレージメカニズムやAPIを使用する必要がなくなります。このエントリーポイントはnavigator.storage.getDirectory() メソッドです。

2. ファイルとディレクトリ

2.1. 概念

ファイルシステムエントリは、ファイルエントリまたはディレクトリエントリのいずれかです。

各ファイルシステムエントリには、関連付けられたクエリアクセスアルゴリズムがあり、これは"read"または"readwrite" modeを取り、ファイルシステムアクセス結果を返します。 特に明記されていない場合、このアルゴリズムはファイルシステムアクセス結果を返し、その許可状態は"denied"であり、エラー名は空の文字列です。

各ファイルシステムエントリには、関連付けられたリクエストアクセスアルゴリズムがあり、これは"read"または"readwrite" modeを取り、ファイルシステムアクセス結果を返します。 特に明記されていない場合、このアルゴリズムはファイルシステムアクセス結果を返し、その許可状態は"denied"であり、エラー名は空の文字列です。

ファイルシステムアクセス結果は、構造体であり、クエリアクセスまたはリクエストアクセスに基づいてファイルシステムへのアクセス結果をカプセル化します。 以下の項目を持ちます。

許可状態

PermissionState

エラー名

文字列であり、 許可状態が "granted"の場合は空文字列でなければなりません。それ以外の場合は、 名前であり、 DOMException名テーブルにリストされています。 通常、許可状態が "granted"でない場合、この値は "NotAllowedError"であるべきです。

従属する仕様では、このAPIを強力な機能と見なす場合があります。ただし、他の強力な機能のように許可リクエストアルゴリズムが例外をスローする可能性がある場合とは異なり、ファイルシステムエントリのクエリアクセスやリクエストアクセスアルゴリズムは並列で ファイルシステムキュー上で実行されなければならず、例外をスローすることは許されません。代わりに、呼び出し側はこれらのアルゴリズムが空文字列以外のエラー名を返した場合に適切にストレージタスクをキューにいれることで拒否することが期待されます。

注: この仕様のみを実装し、従属する仕様を実装していない場合、ファイルシステムエントリのクエリアクセスやリクエストアクセスを実装する必要はありません。

FileSystemHandleに関連付けられたアクセスチェックアルゴリズムを作成する必要があります。[Issue #101]

各ファイルシステムエントリには関連付けられた名前 (a 文字列)があります。

有効なファイル名は、文字列であり、空文字列ではなく、"."または".."に等しくなく、'/'や基盤となるプラットフォームでパス区切り文字として使用されるその他の文字を含まないものです。

注: これは、Windowsでは名前に'\'を含めることができませんが、他のオペレーティングシステムでは許可される可能性があることを意味します。さらに、基盤となるファイルシステムには、許可される名前に関してさらなる制限がある可能性があるため、文字列が単に有効なファイル名であることは、その名前でファイルやディレクトリを作成することが成功する保証ではありません。

このAPIを使用して決して許可されないファイル名に関して、基盤となるファイルシステムに完全に任せるのではなく、さらに規範的な制限を設けることを検討する必要があります。

ファイルエントリはさらに以下を構成要素として持っています: バイナリデータ (バイトシーケンス)、変更タイムスタンプ (UNIXエポックからのミリ秒数を表す数値)、ロック (open、taken-exclusive、taken-sharedのいずれかである可能性がある文字列)、および共有ロック数 (ある時点で取られている共有ロックの数を表す数値)。

ユーザーエージェントには関連付けられたファイルシステムキューがあり、これがすべてのファイルシステム操作に使用される新しい並列キューを開始するの結果です。

与えられたファイルエントリ file 上で、"exclusive" または "shared" である value を用いてロックを取るためのアルゴリズムは、次の手順で実行されます:
  1. lockをfileに関連付けられたロックとします。

  2. countをfileに関連付けられた共有ロック数とします。

  3. もしvalueが"exclusive"の場合:

    1. もしlockが"open"の場合:

      1. ロックを"taken-exclusive"に設定します。

      2. "success"を返します。

  4. もしvalueが"shared"の場合:

    1. もしlockが"open"の場合:

      1. lockを"taken-shared"に設定します。

      2. countを1に設定します。

      3. "success"を返します。

    2. それ以外でlockが"taken-shared"の場合:

      1. countを1増やします。

      2. "success"を返します。

  5. "failure"を返します。

注: これらの手順はファイルシステムキュー上で実行される必要があります。

与えられたファイルエントリ file のロックを解放するためのアルゴリズムは、次の手順で実行されます:
  1. lockをfileに関連付けられたロックとします。

  2. countをfileに関連付けられた共有ロック数とします。

  3. もしlockが"taken-shared"の場合:

    1. countを1減らします。

    2. もしcountが0の場合、lockを"open"に設定します。

  4. それ以外の場合、lockを"open"に設定します。

注: これらの手順はファイルシステムキュー上で実行される必要があります。

注: ロックはファイルへの同時変更を防ぐのに役立ちます。FileSystemWritableFileStream は共有ロックを必要とし、FileSystemSyncAccessHandle は排他的なロックを必要とします。

ディレクトリエントリはさらに、集合としての 子要素を含み、これらもファイルシステムエントリです。 各要素はファイルエントリまたはディレクトリエントリのいずれかです。

ファイルシステムエントリ entryは、最大で1つのディレクトリエントリの子要素として含まれるべきであり、そのディレクトリエントリはentryの 親としても知られています。 ファイルシステムエントリの親は、そのようなディレクトリエントリが存在しない場合はnullです。

注: 異なるファイルシステムエントリが、ディスク上で同じファイルまたはディレクトリを表す場合があり、 その場合、両方のエントリが異なる親を持つか、または一方が親を持ち、もう一方が親を持たない場合があります。

ファイルシステムエントリは(必須ではありませんが)ホストオペレーティングシステムのローカルファイルシステム上のファイルに裏付けされることがあり、 そのため、バイナリデータ、変更タイムスタンプ、 および子要素がこの仕様外のアプリケーションによって変更される可能性があります。 外部の変更がこの仕様で定義されたデータ構造にどのように反映されるか、 またこの仕様で定義されたデータ構造に加えられた変更が外部にどのように反映されるかは、個々のユーザーエージェントの実装に委ねられています。

ある ファイルシステムエントリー a と 同じエントリー である ファイルシステムエントリー b とは、 a そのものである b、または a と b がローカルファイルシステム上の同じファイルまたはディレクトリを実体としている場合のエントリーをいう。

次の手順で 解決する 対象は、 ファイルシステム ロケーター child であり、基準は ディレクトリロケーター root である:

  1. result を 新しい プロミス とする。

  2. 以下の手順をエンキューする 先は ファイルシステム キュー とする:

    1. もし child の ロケーター の ルート と異なるのが root の ロケーター の ルート なら、 解決 を result に対して null で行い、これらの手順を中止する。

    2. childPath を child の ロケーター の パス とする。

    3. rootPath を root の ロケーター の パス とする。

    4. もし childPath と 同じパス なのが rootPath なら、 解決 を result に対して « » で行い、これらの手順を中止する。

    5. もし rootPath の サイズ が 上回るのが childPath の サイズ なら、 解決 を result に対して null で行い、これらの 手順を中止する。

    6. 各 index (rootPath の インデックス)について:

      1. もし rootPath.[[index]] と異なるのが childPath.[[index]] なら、 解決 を result に対して null で行い、中止するのは これらの手順とする。

    7. relativePath を « » とする。

    8. 各 index (次の 範囲 内の値: 開始値は rootPath の サイズ、 終了値は childPath の サイズ とし、 終了値を含まない)について、 追加 する値は childPath.[[index]]、追加先は relativePath とする。

    9. 解決 を result に対して relativePath で行う。

  3. result を返す。

ファイルシステムロケータ は、ファイルシステムエントリの潜在的な場所を表します。ファイルシステムロケータは、ファイルロケータ またはディレクトリロケータのいずれかです。

各ファイルシステムロケータには、関連付けられたパス (ファイルシステムパス)、 種類 (FileSystemHandleKind)、 およびルート (ファイルシステムルート)があります。

各ロケータにストレージバケットを与えることを検討してください。 [Issue #109]

ファイルロケータは、ファイルシステムロケータであり、 その種類が"file"です。 ディレクトリロケータは、ファイルシステムロケータであり、 その種類が"directory"です。

ファイルシステムルートは、 不透明な文字列であり、 その値は実装依存です。

ファイルシステムロケータ locatorが、 位置付ける ファイルエントリ entryが、 バケットファイルシステムのルートディレクトリに対して data/drafts/example.txtというパスに概念的に存在する場合、 locatorの種類は"file"でなければならず、 locatorのパスは« "data", "drafts", "example.txt" »でなければならず、 locatorのルートには、 ストレージバケットやディスクドライブのような関連する識別情報を含む場合があります。

ファイルシステムロケータ aは、 ファイルシステムロケータ bと同じロケータであるとみなされます。 それはaの種類が bの種類と同じであり、 aのルートが bのルートと同じであり、 aのパスが 同じパスとしてbのパスと一致する場合です。

エントリを特定するアルゴリズムは、 ファイルシステムロケータ locatorを受け取り、 以下の実装依存の手順を実行します:
ロケータを取得するアルゴリズムは、 ファイルシステムエントリ entryを受け取り、 以下の実装依存の手順を実行します:

ファイルシステムパスは、 1つ以上の項目で構成される、 文字列のリストです。 これは、ディスクまたはメモリ上の実際の場所にマッピングされた仮想パスである場合や、 ローカルファイルシステム上のパスに直接対応する場合、 またはディスク上のいかなるファイルにも対応しない場合があります。 対応するファイルシステムエントリの実際の物理的位置は、 実装依存です。

pathをリスト « "data", "drafts", "example.txt" »とします。 ディスク上にexample.txtという名前のファイルが存在するという期待はありません。

ある ファイルシステムパス a と 同じパス である ファイルシステムパス b とは a に等しい b をいう。

ファイルシステムロケータ の内容、特にその パス は、ウェブサイトプロセスに完全に共有されることは想定されていません。 ファイルシステムパス には、ファイルシステムロケータ が後に親 ディレクトリロケータ を基点として 解決 されない限り、ウェブサイトに知られていない可能性のあるコンポーネントが含まれている場合があります。

2.2. FileSystemHandle インターフェイス

enum FileSystemHandleKind {
  "file",
  "directory",
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemHandle {
  readonly attribute FileSystemHandleKind kind;
  readonly attribute USVString name;

  Promise<boolean> isSameEntry(FileSystemHandle other);
};

FileSystemHandle オブジェクトはロケータ( ファイルシステムロケータ) に関連付けられています。

注: 複数のFileSystemHandle オブジェクトは同じファイルシステムロケーターを持つことがあります。

FileSystemHandle バケットファイルシステム内にある のは、その ロケータ の パス の最初の 項目 が空文字列である場合です。

注: これは少し特殊ですが、バケットファイルシステムのルートディレクトリのみが 空文字列を含むパスを持つことができるため、機能します。 パスのその他の項目はすべて有効なファイル名である必要があります。 詳細はgetDirectory() を参照してください。

各ロケータにストレージバケットを付与することで、この状況を改善することを検討してください。[Issue #109]

FileSystemHandle オブジェクトはシリアライズ可能なオブジェクトです。

以下は、value、serialized、およびforStorageを与えられたときのシリアライズ手順です。

  1. serialized.[[Origin]]をvalueの関連する設定オブジェクトのオリジンに設定します。

  2. serialized.[[Locator]]をvalueのロケータに設定します。

以下は、serializedおよびvalueを与えられたときのデシリアライズ手順です。
  1. もしserialized.[[Origin]]がvalueの関連する設定オブジェクトのオリジンと同一オリジンでない場合、 "DataCloneError" DOMException をスローします。

  2. valueのロケータをserialized.[[Locator]]に設定します。

handle . kind

もしhandleがFileSystemFileHandle であれば、"file"を返し、 もしhandleがFileSystemDirectoryHandle であれば、"directory"を返します。

これは、ディレクトリの内容を反復処理する際にファイルとディレクトリを区別するために使用できます。

handle . name

handleのロケータのパスの最後のパスコンポーネントを返します。

kindのゲッター手順は、 thisのロケータの種類を返します。

nameのゲッター手順は、 thisのロケータのパスの最後の項目(文字列)を返します。

2.2.1. isSameEntry() メソッド

same = await handle1 . isSameEntry( handle2 )

もし handle1 と handle2 が同じファイルまたはディレクトリを表す場合、true を返します。

isSameEntry(other)メソッドの手順は以下の通りです:
  1. realm を this の 関連する Realm とする。

  2. p を 新しい Promise として realm 内で生成する。

  3. 以下の手順を ファイルシステムキュー にエンキューする:

    1. もし this の locator が 同じ locator を other の locator として持つ場合、 resolve p を true で解決する。

    2. それ以外の場合、resolve p を false で解決する。

  4. p を返す。

2.3. FileSystemFileHandle インターフェース

dictionary FileSystemCreateWritableOptions {
  boolean keepExistingData = false;
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemFileHandle : FileSystemHandle {
  Promise<File> getFile();
  Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWritableOptions options = {});
  [Exposed=DedicatedWorker]
  Promise<FileSystemSyncAccessHandle> createSyncAccessHandle();
};

Note: FileSystemFileHandle の関連する locator の kind は "file" です。

directory locator parentLocator と文字列 name を与えられた場合に、Realm realm 内で 子の FileSystemFileHandle を作成する 手順は次の通りです:
  1. handle を 新しい FileSystemFileHandle として realm 内で生成する。

  2. childType を "file" とする。

  3. childRoot を parentLocator の root のコピーとする。

  4. childPath を clone した parentLocator の path に append した name とする。

  5. handle の locator を以下の ファイルシステムロケータ として設定する:その kind は childType、 root は childRoot、そして path は childPath。

  6. handle を返す。

新しい FileSystemFileHandleを作成するには、 ファイルシステムルート root およびファイル システムパス path が与えられた場合、 Realm realm 内で以下を行う:
  1. handle を新しい FileSystemFileHandle として realm 内に作成する。

  2. handle のロケーターを、ファイルシステムロケーターであって、その 種別が "file"、 ルートが root であり、かつ パスが path であるものに設定する。

  3. handle を返す。

FileSystemFileHandle オブジェクトはシリアライズ可能なオブジェクトである。そのシリアライズ手順および デシリアライズ手順は、FileSystemHandleのものと同じである。

2.3.1. getFile() メソッド

file = await fileHandle . getFile()

File を返し、handle の locator によって 見つけることができる ファイルエントリ のディスク上の状態を表します。 このメソッド呼び出し後にディスク上のファイルが変更または削除された場合、返された File オブジェクトはおそらく読み取れなくなる可能性があります。

getFile()メソッドの手順は以下の通りです:
  1. result を 新しい Promise として作成する。

  2. locator を this の locator とする。

  3. global を this の 関連するグローバルオブジェクト とする。

  4. 以下の手順をエンキューする ファイルシステムキュー に:

    1. entry を、エントリを見つける手順を locator に対して実行した結果とする。

    2. accessResult を、entry の query access に "read" を渡して実行した結果とする。

    3. ストレージタスクをキューに追加する global を使用して以下を実行:

      1. もし accessResult の permission state が "granted" でない場合、 reject result を accessResult の エラー名 を持つ DOMException として解決し、これらの手順を中止する。

      2. もし entry が null の場合、reject result を "NotFoundError" DOMException として解決し、これらの手順を中止する。

      3. アサート: entry が ファイルエントリ であること。

      4. f を新しい File とする。

      5. f の スナップショット状態 を entry の現在の状態に設定する。

      6. f の基礎となるバイトシーケンスを entry の バイナリデータ のコピーに設定する。

      7. f の name を entry の name に設定する。

      8. f の lastModified を entry の 修正タイムスタンプ に設定する。

      9. f の type を例えば entry の name やそのファイル拡張子に基づく 実装依存の値 に設定する。

        読み取りおよびスナップショットの動作仕様については、[FILE-API] 仕様にてさらに詳細に記述する必要がありますが、 現在のところは曖昧なままです。

      10. resolve result を f で解決する。

  5. result を返す。

2.3.2. createWritable() メソッド

stream = await fileHandle . createWritable()
stream = await fileHandle . createWritable({ keepExistingData: true/false })

FileSystemWritableFileStream を返し、ファイルに書き込みを行うために使用できます。stream を通じて行われた変更は、 fileHandle の locator により 見つけることのできる ファイルエントリ に、stream が 閉じられるまで反映されません。ユーザーエージェントは部分的な書き込みが発生しないようにします。 つまり、ファイルは古い内容を保持するか、stream を通じて書き込まれたすべてのデータを含むかのいずれかになります。

これは通常、一時ファイルにデータを書き込むことで実装され、書き込みストリームが閉じられたときに、 fileHandle の locator により 見つけることのできる ファイルエントリ を一時ファイルで置き換えることで行われます。

もし keepExistingData が false または指定されていない場合、一時ファイルは空の状態で開始されます。 それ以外の場合、既存のファイルは最初にこの一時ファイルにコピーされます。

FileSystemWritableFileStream を作成することは、fileHandle の locator により 見つけることのできる ファイルエントリ に対して 共有ロック を取得します。 これにより、ストリームが閉じられるまで、エントリに対する FileSystemSyncAccessHandles の作成が防止されます。

createWritable の「inPlace」モード(例えば、大きなファイルやデータベースのようなものの インプレースな変更をサポートするために、書き込みが行われるたびに基礎となる実際のファイルに変更を書き込む)に関する議論や要望については、 WICG/file-system-access issue #67 を参照してください。 これは現在 Chrome では実装されていません。 これを実装するには、マルウェアチェックを実行したいという要望と、 既存の大きなファイルに対して迅速なインプレース変更を行いたいという要望をどのように組み合わせるかを 解決する必要があります。インプレース書き込みは バケットファイルシステム 内のファイルに対して、 FileSystemSyncAccessHandle インターフェースを介して利用可能です。

createWritable(options) メソッドの手順は以下の通りです:
  1. result を 新しい Promise として作成する。

  2. locator を this の locator とする。

  3. realm を this の 関連する Realm とする。

  4. global を this の 関連するグローバルオブジェクト とする。

  5. 以下の手順をエンキューする ファイルシステムキュー に:

    1. entry を、エントリを見つける手順を locator に対して実行した結果とする。

    2. accessResult を、entry の request access に "readwrite" を渡して実行した結果とする。

    3. もし accessResult の permission state が "granted" でない場合、 ストレージタスクをキューに追加する global を使用して、 reject result を accessResult の エラー名 を持つ DOMException として解決し、これらの手順を中止する。

    4. もし entry が null の場合、 ストレージタスクをキューに追加する global を使用して、 reject result を "NotFoundError" DOMException として解決し、これらの手順を中止する。

    5. アサート: entry が ファイルエントリ であること。

    6. lockResult を、ロックを取得する 手順を "shared" を指定して entry に対して実行した結果とする。

    7. ストレージタスクをキューに追加する global を使用して以下を実行:

      1. もし lockResult が "failure" の場合、 reject result を "NoModificationAllowedError" DOMException として解決し、これらの手順を中止する。

      2. stream を、新しい FileSystemWritableFileStream を作成する 手順を entry に対して realm 内で実行した結果とする。

      3. もし options["keepExistingData"] が true の場合:

        1. stream の [[buffer]] を、 entry の バイナリデータ のコピーに設定する。

      4. resolve result を stream で解決する。

  6. result を返す。

2.3.3. createSyncAccessHandle() メソッド

handle = await fileHandle . createSyncAccessHandle()

FileSystemSyncAccessHandle を返し、ファイルの読み取り/書き込みに使用できます。 handle を通じて行われた変更は、fileHandle の locator により 見つけることができる ファイルエントリ に即座に反映される場合があります。 この変更をファイルに確実に反映するには、ハンドルをフラッシュすることができます。

FileSystemSyncAccessHandle を作成すると、 fileHandle の locator により 見つけることができる ファイルエントリ に対して 排他ロック を取得します。 これにより、アクセスハンドルが閉じられるまで、エントリに対する追加の FileSystemSyncAccessHandles または FileSystemWritableFileStreams の作成が防止されます。

返された FileSystemSyncAccessHandle は同期メソッドを提供します。これにより、非同期操作に高いオーバーヘッドが伴うコンテキスト(例えば、WebAssembly)での 高パフォーマンスが可能になります。

現在、このメソッドは fileHandle が バケットファイルシステム内にある 場合にのみ成功します。

createSyncAccessHandle() メソッドの手順は以下の通りです:
  1. result を 新しい Promise として作成する。

  2. locator を this の locator とする。

  3. realm を this の 関連する Realm とする。

  4. global を this の 関連するグローバルオブジェクト とする。

  5. isInABucketFileSystem を true とし、this が バケットファイルシステム内にある かどうかを判定する。それ以外の場合は false とする。

  6. 以下の手順をエンキューする ファイルシステムキュー に:

    1. entry を、エントリを見つける手順を locator に対して実行した結果とする。

    2. accessResult を、entry の request access に "readwrite" を渡して実行した結果とする。

    3. もし accessResult の permission state が "granted" でない場合、 ストレージタスクをキューに追加する global を使用して、 reject result を accessResult の エラー名 を持つ DOMException として解決し、これらの手順を中止する。

    4. もし isInABucketFileSystem が false の場合、 ストレージタスクをキューに追加する global を使用して、 reject result を "InvalidStateError" DOMException として解決し、これらの手順を中止する。

    5. もし entry が null の場合、 ストレージタスクをキューに追加する global を使用して、 reject result を "NotFoundError" DOMException として解決し、これらの手順を中止する。

    6. アサート: entry が ファイルエントリ であること。

    7. lockResult を、ロックを取得する 手順を "exclusive" を指定して entry に対して実行した結果とする。

    8. ストレージタスクをキューに追加する global を使用して以下を実行:

      1. もし lockResult が "failure" の場合、 reject result を "NoModificationAllowedError" DOMException として解決し、これらの手順を中止する。

      2. handle を、新しい FileSystemSyncAccessHandle を作成する 手順を entry に対して realm 内で実行した結果とする。

      3. resolve result を handle で解決する。

  7. result を返す。

2.4. FileSystemDirectoryHandle インターフェース

dictionary FileSystemGetFileOptions {
  boolean create = false;
};

dictionary FileSystemGetDirectoryOptions {
  boolean create = false;
};

dictionary FileSystemRemoveOptions {
  boolean recursive = false;
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemDirectoryHandle : FileSystemHandle {
  async_iterable<USVString, FileSystemHandle>;

  Promise<FileSystemFileHandle> getFileHandle(USVString name, optional FileSystemGetFileOptions options = {});
  Promise<FileSystemDirectoryHandle> getDirectoryHandle(USVString name, optional FileSystemGetDirectoryOptions options = {});

  Promise<undefined> removeEntry(USVString name, optional FileSystemRemoveOptions options = {});

  Promise<sequence<USVString>?> resolve(FileSystemHandle possibleDescendant);
};

注意: FileSystemDirectoryHandle の関連する locator の kind は "directory" です。

directory locator parentLocator と文字列 name を与えられた場合に、Realm realm 内で 子の FileSystemDirectoryHandle を作成する 手順は次の通りです:
  1. handle を 新しい FileSystemDirectoryHandle として realm 内で生成する。

  2. childType を "directory" とする。

  3. childRoot を parentLocator の root のコピーとする。

  4. childPath を clone した parentLocator の path に append した name とする。

  5. handle の locator を以下の ファイルシステムロケータ として設定する:その kind は childType、 root は childRoot、そして path は childPath。

  6. handle を返す。

file system root root と file system path path を与えられた場合に、Realm realm 内で 新しい FileSystemDirectoryHandle を作成する 手順は次の通りです:
  1. handle を 新しい FileSystemDirectoryHandle として realm 内で生成する。

  2. handle の locator を以下の ファイルシステムロケータ として設定する:その kind は "directory"、 root は root、そして path は path。

  3. handle を返す。

FileSystemDirectoryHandle オブジェクトはシリアライズ可能なオブジェクトです。それらのシリアライズ手順および デシリアライズ手順はFileSystemHandle と同じです。

2.4.1. ディレクトリの反復処理

for await (let [name, handle] of directoryHandle) {}
for await (let [name, handle] of directoryHandle . entries()) {}
for await (let handle of directoryHandle . values()) {}
for await (let name of directoryHandle . keys()) {}

directoryHandle の locator により 見つけることができる ディレクトリエントリ を親とするすべてのエントリを反復処理します。 反復処理中に作成または削除されたエントリが含まれるかどうかは保証されません。

将来的には、再帰的反復処理などをサポートするために async_iterable 宣言に引数を追加することを検討するかもしれません。[Issue #15]

非同期イテレータ初期化手順 に基づいて FileSystemDirectoryHandle handle とその非同期イテレータ iterator を初期化する手順は次の通りです:
  1. iterator の 過去の結果 を空の セット に設定する。

次の反復結果を取得するための FileSystemDirectoryHandle handle とその非同期イテレータ iterator の手順は次の通りです:
  1. promise を 新しい Promise として作成する。

  2. 以下の手順をエンキューする ファイルシステムキュー に:

    1. directory を、エントリを見つける 手順を handle の locator に対して実行した結果とする。

    2. accessResult を、directory の query access に "read" を渡して実行した結果とする。

    3. ストレージタスクをキューに追加する handle の 関連するグローバルオブジェクト を使用して以下を実行:

      1. もし accessResult の permission state が "granted" でない場合、 reject promise を accessResult の エラー名 を持つ DOMException として解決し、これらの手順を中止する。

      2. もし directory が null の場合、 reject promise を "NotFoundError" DOMException として解決し、これらの手順を中止する。

        1. アサート: directory が ディレクトリエントリ であること。

      3. child を directory の 子 の中から、child の name が iterator の 過去の結果 に 含まれていないファイルシステムエントリとして選び、それが存在しない場合は null とする。

        注意: これは意図的に反復順序について非常に曖昧にしています。 異なるプラットフォームやファイルシステムは反復順序について異なる保証を提供し、すべてのプラットフォームで効率的に実装できるようにしたいと考えています。 したがって、どの順序で要素が返されるかについては一切保証されません。

      4. もし child が null の場合、 resolve promise を undefined として解決し、これらの手順を中止する。

      5. 追加 child の name を iterator の 過去の結果 に。

      6. もし child が ファイルエントリ である場合:

        1. result を 子の FileSystemFileHandle を作成する 手順を handle の locator と child の name に対して handle の 関連する Realm 内で実行した結果とする。

      7. それ以外の場合:

        1. result を 子の FileSystemDirectoryHandle を作成する 手順を handle の locator と child の name に対して handle の 関連する Realm 内で実行した結果とする。

      8. resolve promise を (child の name, result) で解決する。

  3. promise を返す。

2.4.2. getFileHandle() メソッド

fileHandle = await directoryHandle . getFileHandle(name)
fileHandle = await directoryHandle . getFileHandle(name, { create: false })

directoryHandle の ロケーター によって 位置特定可能 な ディレクトリエントリ 内の name という名前のファイルのハンドルを返します。 存在しない場合、この操作は拒否されます。

fileHandle = await directoryHandle . getFileHandle(name, { create: true })

directoryHandle の ロケーター によって 位置特定可能 な ディレクトリエントリ 内の name という名前のファイルのハンドルを返します。 存在しない場合、この操作は新しいファイルを作成します。 name という名前のファイルを作成できない場合、この操作は拒否されます。 作成は、同じ名前を持つディレクトリが既に存在する、基盤となるファイルシステムでサポートされていない文字を名前に使用している、またはユーザーエージェントがセキュリティ上の理由でファイルの作成を許可しなかったために失敗することがあります。

この操作には書き込み権限が必要です。返されるファイルが既に存在する場合でも同様です。 このハンドルが既に書き込み権限を持っていない場合、ユーザーにプロンプトを表示する可能性があります。 書き込み権限を必要とせずに既存のファイルを取得するには、このメソッドを { create: false } と共に呼び出してください。

この getFileHandle(name, options) メソッドの手順は次のとおりである:
  1. result を 新しい プロミス とする。

  2. realm を this の 関連する Realm とする。

  3. locator を this の ロケーター とする。

  4. global を this の 関連するグローバルオブジェクト とする。

  5. 以下の手順をエンキューする 先は ファイルシステム キュー とする:

    1. もし name が 有効なファイル名 でなければ、ストレージタスクをキューに入れる 際に global を用い、タスクでは 拒否 を result に対して TypeError で行う。そして これらの手順を中止する。

    2. entry を次の結果とする:エントリーの特定 に locator を与える。

    3. もし options["create"] が true なら:

      1. accessResult を、次を実行した結果とする:entry の アクセス要求 に "readwrite" を与える。

    4. そうでなければ:

      1. accessResult を、次を実行した結果とする:entry の アクセス照会 に "read" を与える。

    5. ストレージタスクをキューに入れる 際に global を用い、 そのタスクで次の手順を実行する:

      1. もし accessResult の 許可 状態 と異なるのが "granted" なら、 拒否 を result に対して次の DOMException (名前は accessResult の エラー名)で行い、中止するのは これらの手順とする。

      2. もし entry が null なら、拒否 を result に対して "NotFoundError" DOMException で行い、これらの手順を中止する。

      3. 表明: entry は ディレクトリ エントリー である。

      4. 各 child ( entry の 子)について:

        1. もし child の 名前 が name に等しいなら:

          1. もし child が ディレクトリ エントリー なら:

            1. 拒否 を result に対して "TypeMismatchError" DOMException で行い、これらの手順を中止する。

          2. 解決 を result に対して行う。値は 次の結果とする: 作成( 子 FileSystemFileHandle) を、 locator と child の 名前 を用いて realm 内で行う。そして これらの手順を中止する。

      5. もし options["create"] が false なら:

        1. 拒否 を result に対して "NotFoundError" DOMException で行い、 これらの手順を中止する。

      6. child を新しい ファイルエントリー とし、その アクセス照会 および アクセス要求 アルゴリズムは、 entry のものとする。

      7. child の 名前 を name に設定する。

      8. child の バイナリデータ を空の バイト列 に設定する。

      9. child の 変更タイムスタンプ を現在時刻に設定する。

      10. 追加 する値は child、追加先は entry の 子 とする。

      11. 基盤となるファイルシステム内での child の作成が例外をスローするなら、 拒否 を result に対してその例外で行い、 これらの手順を中止する。

        これがスローし得る例外を、より明確に規定する必要がある。 [課題 #11]

      12. 解決 を result に対して行う。値は次の結果とする: 作成(子 FileSystemFileHandle) を locator と child の 名前 を用いて realm 内で行う。

  6. result を返す。

2.4.3. getDirectoryHandle() メソッド

subdirHandle = await directoryHandle . getDirectoryHandle(name)
subdirHandle = await directoryHandle . getDirectoryHandle(name, { create: false })

directoryHandle の ロケーター によって 位置特定可能 な ディレクトリエントリ 内の name という名前のディレクトリのハンドルを返します。 存在しない場合、この操作は拒否されます。

subdirHandle = await directoryHandle . getDirectoryHandle(name, { create: true })

directoryHandle の ロケーター によって 位置特定可能 な ディレクトリエントリ 内の name という名前のディレクトリのハンドルを返します。 存在しない場合、新しいディレクトリを作成します。 ディレクトリの作成に失敗した場合、この操作は拒否されます。 作成は、同じ名前を持つファイルが既に存在する場合、または基盤となるファイルシステムでサポートされていない文字を名前に使用している場合に失敗することがあります。

この操作には書き込み権限が必要です。返されるディレクトリが既に存在する場合でも同様です。 このハンドルが既に書き込み権限を持っていない場合、ユーザーにプロンプトを表示する可能性があります。 書き込み権限を必要とせずに既存のディレクトリを取得するには、このメソッドを { create: false } と共に呼び出してください。

この getDirectoryHandle(name, options) メソッドの手順は次のとおりである:
  1. result を 新しい プロミス とする。

  2. realm を this の 関連する Realm とする。

  3. locator を this の ロケーター とする。

  4. global を this の 関連するグローバルオブジェクト とする。

  5. 以下の手順をエンキューする 先は ファイルシステム キュー とする:

    1. もし name が 有効なファイル名 でなければ、ストレージタスクをキューに入れる 際に global を用い、タスクでは 拒否 を result に対して TypeError で行う。そして これらの手順を中止する。

    2. entry を次の結果とする:エントリーの特定 に locator を与える。

    3. もし options["create"] が true なら:

      1. accessResult を、次を実行した結果とする:entry の アクセス要求 に "readwrite" を与える。

    4. そうでなければ:

      1. accessResult を、次を実行した結果とする:entry の アクセス照会 に "read" を与える。

    5. ストレージタスクをキューに入れる 際に global を用い、 そのタスクで次の手順を実行する:

      1. もし accessResult の 許可 状態 と異なるのが "granted" なら、 拒否 を result に対して次の DOMException (名前は accessResult の エラー名)で行い、中止するのは これらの手順とする。

      2. もし entry が null なら、拒否 を result に対して "NotFoundError" DOMException で行い、これらの手順を中止する。

      3. 表明: entry は ディレクトリ エントリー である。

      4. 各 child ( entry の 子)について:

        1. もし child の 名前 が name に等しいなら:

          1. もし child が ファイルエントリー なら:

            1. 拒否 を result に対して "TypeMismatchError" DOMException で行い、これらの手順を中止する。

          2. 解決 を result に対して行う。値は 次の結果とする: 作成( 子 FileSystemDirectoryHandle) を、 locator と child の 名前 を用いて realm 内で行う。そして これらの手順を中止する。

      5. もし options["create"] が false なら:

        1. 拒否 を result に対して "NotFoundError" DOMException で行い、 これらの手順を中止する。

      6. child を新しい ディレクトリエントリー とし、その アクセス照会 および アクセス要求 アルゴリズムは、 entry のものとする。

      7. child の 名前 を name に設定する。

      8. child の 子 を空の 集合 に設定する。

      9. 追加 する値は child、追加先は entry の 子 とする。

      10. 基盤となるファイルシステム内での child の作成が例外をスローするなら、 拒否 を result に対してその例外で行い、 これらの手順を中止する。

        これがスローし得る例外を、より明確に規定する必要がある。 [課題 #11]

      11. 解決 を result に対して行う。値は次の結果とする: 作成(子 FileSystemDirectoryHandle) を、 locator と child の 名前 を用いて realm 内で行う。

  6. result を返す。

2.4.4. removeEntry() メソッド

await directoryHandle . removeEntry(name)
await directoryHandle . removeEntry(name, { recursive: false })

directoryHandle の ロケーター によって 位置特定可能 な ディレクトリエントリ に name という名前のファイル、または空のディレクトリが含まれている場合、これを削除しようとします。

存在しないファイルやディレクトリを削除しようとする場合は成功とみなされますが、空でないディレクトリを削除しようとするとプロミスが拒否されます。

await directoryHandle . removeEntry(name, { recursive: true })

directoryHandle の ロケーター によって 位置特定可能 な ディレクトリエントリ 内にある name という名前のファイルシステムエントリを削除します。 そのエントリがディレクトリの場合、その内容も再帰的に削除されます。

存在しないファイルやディレクトリを削除しようとする場合は成功とみなされます。

この removeEntry(name, options) メソッドの手順は次のとおりである:
  1. result を 新しい プロミス とする。

  2. locator を this の ロケーター とする。

  3. global を this の 関連するグローバルオブジェクト とする。

  4. 以下の手順をエンキューする 先は ファイルシステム キュー とする:

    1. もし name が 有効なファイル名 でなければ、ストレージタスクをキューに入れる 際に global を用い、タスクでは 拒否 を result に対して TypeError で行う。そして これらの手順を中止する。

    2. entry を次の結果とする:エントリーの特定 に locator を与える。

    3. accessResult を、次を実行した結果とする:entry の アクセス要求 に "readwrite" を与える。

    4. ストレージタスクをキューに入れる 際に global を用い、 そのタスクで次の手順を実行する:

      1. もし accessResult の 許可 状態 と異なるのが "granted" なら、 拒否 を result に対して次の DOMException (名前は accessResult の エラー名)で行い、中止するのは これらの手順とする。

      2. もし entry が null なら、拒否 を result に対して "NotFoundError" DOMException で行い、これらの手順を中止する。

      3. 表明: entry は ディレクトリ エントリー である。

      4. 各 child ( entry の 子)について:

        1. もし child の 名前 が name に等しいなら:

          1. もし child が ディレクトリ エントリー なら:

            1. もし child の 子 が 空 でなく、かつ options["recursive"] が false なら:

              1. 拒否 を result に対して "InvalidModificationError" DOMException で行い、 これらの手順を中止する。

          2. 除去 する対象を child とし、 entry の 子 から除去する。

          3. 基盤となるファイルシステム内での child の削除によってスローされたものが 何らかの 例外なら、拒否 を result に対してその 例外で行い、 これらの手順を中止する。

            注記: もし recursive が true なら、削除は失敗することがあり、 その失敗はアトミックではない場合がある。一部のファイルやディレクトリが 削除されても、他のファイル やディレクトリが残っている可能性がある。

            より明確に規定する必要がある、これがどのような例外を スローし得るかを。 [課題 #11]

          4. 解決 を result に対して undefined で行う。

      5. 拒否 を result に対して "NotFoundError" DOMException で行う。

  5. result を返す。

2.4.5. resolve() メソッド

path = await directory . resolve( child )

もし child が directory と等しい場合、path は空の配列になります。

もし child が directory の直接の子である場合、path は child の名前を含む配列になります。

もし child が directory の子孫である場合、path は すべての中間ディレクトリの名前と child の名前を最後の要素として含む配列になります。 たとえば、directory が /home/user/project を表し、 child が /home/user/project/foo/bar を表す場合、これは ['foo', 'bar'] を返します。

それ以外の場合(directory と child が関連していない場合)、path は null になります。

// 有効なディレクトリハンドルを取得したと仮定します。
const dir_ref = current_project_dir;
if (!dir_ref) return;

// 次にファイルの参照を取得します:
const file_ref = await dir_ref.getFileHandle(filename, { create: true });

// file_ref が dir_ref 内に存在するか確認します:
const relative_path = await dir_ref.resolve(file_ref);
if (relative_path === null) {
    // dir_ref 内に存在しません。
} else {
    // relative_path は名前の配列で、dir_ref から file_ref を表すファイルまでの相対パスを示します:
    assert relative_path.pop() === file_ref.name;

    let entry = dir_ref;
    for (const name of relative_path) {
        entry = await entry.getDirectory(name);
    }
    entry = await entry.getFile(file_ref.name);

    // |entry| はディスク上で |file_ref| と同じファイルを表します。
    assert await entry.isSameEntry(file_ref) === true;
}
The resolve(possibleDescendant) メソッドの手順は possibleDescendant の ロケーター を this の ロケーター に対して 解決 した結果を返すことです。

2.5. FileSystemWritableFileStream インターフェース

enum WriteCommandType {
  "write",
  "seek",
  "truncate",
};

dictionary WriteParams {
  required WriteCommandType type;
  unsigned long long? size;
  unsigned long long? position;
  (BufferSource or Blob or USVString)? data;
};

typedef (BufferSource or Blob or USVString or WriteParams) FileSystemWriteChunkType;

[Exposed=(Window,Worker), SecureContext]
interface FileSystemWritableFileStream : WritableStream {
  Promise<undefined> write(FileSystemWriteChunkType data);
  Promise<undefined> seek(unsigned long long position);
  Promise<undefined> truncate(unsigned long long size);
};

FileSystemWritableFileStream は [[file]](ファイルエントリ)を関連付けられています。

FileSystemWritableFileStream は [[buffer]](バイトシーケンス)を関連付けられています。 初期状態では空です。

注: このバッファは任意の大きさになり得るため、実装ではこれをメモリ上に保持するのではなく、 一時ファイルを使用することが期待されます。[[buffer]] へのすべてのアクセスはプロミスを返すメソッドとアルゴリズムで行われるため、 操作は同期的に見えますが、実装は非同期的にそれらを実行できます。

FileSystemWritableFileStream は [[seekOffset]](数値)を関連付けられています。 初期値は 0 です。

FileSystemWritableFileStream オブジェクトは、WritableStream オブジェクトに追加の便利なメソッドを備えたもので、ディスク上の単一ファイルを操作します。

作成時に、基盤となるシンクが作成され、ストリームは使用可能になります。 ストリームで実行されるすべての操作はキュー可能であり、プロデューサーはバックプレッシャーに応答することができます。

基盤となるシンクの write メソッド、および WritableStreamDefaultWriter の write() メソッドでは、バイト状のデータまたは WriteParams を入力として受け付けます。

FileSystemWritableFileStream は、ファイルの先頭からバイトオフセット 0 に初期化されたファイル位置カーソルを持っています。 write() を使用するか、WritableStream の機能を通じて WritableStreamDefaultWriter の write() メソッドを使用することで、この位置はストリームオブジェクトを介して書き込まれたバイト数に基づいて進みます。

同様に、ReadableStream を FileSystemWritableFileStream オブジェクトにパイプすると、この位置はストリームを通過したバイト数で更新されます。

getWriter() は、WritableStreamDefaultWriter のインスタンスを返します。

新しい FileSystemWritableFileStream を作成する ために、ファイルエントリ file を Realm realm に与えます:
  1. stream を 新しい FileSystemWritableFileStream として realm 内に作成します。

  2. stream の [[file]] を file に設定します。

  3. writeAlgorithm を chunk 引数を取り、stream および chunk を用いて チャンクを書き込む アルゴリズムを実行した結果を返すアルゴリズムとします。

  4. closeAlgorithm を次の手順とします:

    1. closeResult を 新しいプロミス とします。

    2. 以下の手順をエンキュー して、ファイルシステムキュー に送ります:

      1. accessResult を file の アクセスクエリ を "readwrite" にして実行した結果とします。

      2. ストレージタスクをエンキュー して file の 関連するグローバルオブジェクト に以下の手順を実行させます:

        1. もし accessResult の 権限状態 が "granted" でない場合、 closeResult を、拒否する。このとき、 accessResult のエラー名 を名前に持つ DOMException を拒否理由とし、これらの手順を中止します。

        2. 実装依存 のマルウェアスキャンおよび安全なブラウジングチェックを実行します。 これらのチェックが失敗した場合、プロミスを拒否 して "AbortError" DOMException を 用いてこれらの手順を中止します。

        3. stream の [[file]] の バイナリデータ を stream の [[buffer]] に設定します。 それが例外をスローした場合、プロミスを拒否 してその例外を用いてこれらの手順を中止します。

          注: これは書き込み対象の ディスク上のファイルの内容を原子的に更新することが期待されています。

        4. 以下の手順をエンキュー して、ファイルシステムキュー に送ります:

          1. ロックを解放 して stream の [[file]] に設定します。

          2. ストレージタスクをエンキュー して file の 関連するグローバルオブジェクト に プロミスを解決 して undefined を返します。

    3. closeResult を返します。

  5. abortAlgorithm を次の手順とします:

    1. 以下の手順をエンキュー して、ファイルシステムキュー に送ります:

      1. ロックを解放 して stream の [[file]] に設定します。

  6. highWaterMark を 1 に設定します。

  7. sizeAlgorithm を 1 を返すアルゴリズムとします。

  8. セットアップ を stream に対して行い、writeAlgorithm を writeAlgorithm に、closeAlgorithm を closeAlgorithm に、abortAlgorithm を abortAlgorithm に、highWaterMark を highWaterMark に、そして sizeAlgorithm を sizeAlgorithm に設定します。

  9. stream を返します。

The write a chunk アルゴリズムは、 FileSystemWritableFileStream stream と chunk を与えられたとき、次の手順を実行する:
  1. input を、converting により chunk を FileSystemWriteChunkType に変換した結果とする。 ここで例外が投げられた場合、その例外で a promise rejected with を返す。

  2. p を a new promise とする。

  3. Enqueue the following steps を file system queue に行う:

    1. accessResult を、 stream の [[file]] の query access を "readwrite" を与えて実行した結果とする。

    2. stream の relevant global object で Queue a storage task し、次を実行する:

      1. もし accessResult の permission state が "granted" でなければ、 reject p with DOMException (accessResult の error name)として、以降の手順を中止する。

      2. command を、input が dictionary であれば input["type"]、 そうでなければ "write" とする。

      3. もし command が "write" なら:

        1. もし input が undefined であるか、または input が dictionary であって input["data"] が exist しないなら、 reject p with TypeError し、以降の手順を中止する。

        2. data を、input が dictionary であれば input["data"]、 そうでなければ input とする。

        3. writePosition を stream の [[seekOffset]] とする。

        4. もし input が dictionary で input["position"] が exists するなら、 writePosition を input["position"] に設定する。

        5. oldSize を stream の [[buffer]] の length とする。

        6. もし data が BufferSource なら、 dataBytes を a copy of data とする。

        7. そうでなく、data が Blob であるなら:

          1. dataBytes を read operation を data に対して実行した結果とする。 ここで例外が投げられたなら、reject p with その例外 とし、以降の手順を中止する。

        8. それ以外の場合:

          1. Assert: data は USVString である。

          2. dataBytes を UTF-8 encoding で data を符号化した結果とする。

        9. もし writePosition が oldSize より大きければ、 writePosition - oldSize 個の 0x00 (NUL) バイトを stream の [[buffer]] の末尾に付加する。

          注: 実装は、飛ばされたファイル内容が実際に NUL バイトで埋められているかのように振る舞うことが期待される。 これはそれらのバイトが実際にディスクへ書き込まれ、ディスク容量を消費しなければならないことを意味しない。 多くのファイルシステムは、これらの NUL バイトが実際のディスク容量を消費しない「スパースファイル」をサポートしている。

        10. head を、byte sequence で stream の [[buffer]] の最初の writePosition バイトを含むものとする。

        11. tail を空の byte sequence とする。

        12. もし writePosition + data の length が oldSize より小さいなら:

          1. tail を、byte sequence で stream の [[buffer]] の 最後の oldSize - (writePosition + data の length) バイトを含むものとする。

        13. stream の [[buffer]] を head、data、および tail の連結に設定する。

        14. 前の手順で stream の [[buffer]] を変更する操作が storage quota 超過で失敗した場合、 reject p with QuotaExceededError とし、以降の手順を中止し、 stream の [[buffer]] を変更しないままにする。

          注: Storage quota は bucket file system に保存されたファイルにのみ適用される。 しかし、この操作は他のファイルでも例えば書き込み先ディスクの容量不足により失敗し得る。

        15. stream の [[seekOffset]] を writePosition + data の length に設定する。

        16. Resolve p。

      4. それ以外で command が "seek" の場合:

        1. Assert: chunk は dictionary である。

        2. もし chunk["position"] が exist しないなら、 reject p with TypeError とし、以降の手順を中止する。

        3. stream の [[seekOffset]] を chunk["position"] に設定する。

        4. Resolve p。

      5. それ以外で command が "truncate" の場合:

        1. Assert: chunk は dictionary である。

        2. もし chunk["size"] が exist しないなら、 reject p with TypeError とし、以降の手順を中止する。

        3. newSize を chunk["size"] とする。

        4. oldSize を stream の [[buffer]] の length とする。

        5. もし newSize が oldSize より大きいなら:

          1. stream の [[buffer]] を、stream の [[buffer]] に、newSize-oldSize 個の 0x00 バイトを含む byte sequence を連結した byte sequence に設定する。

          2. もし直前の手順の操作が storage quota 超過により失敗したなら、 reject p with QuotaExceededError とし、以降の手順を中止し、 stream の [[buffer]] を変更しないままにする。

            注: Storage quota は bucket file system に保存されたファイルにのみ適用される。 しかしこの操作は他のファイルでも、例えば書き込み先ディスクの空き容量不足により失敗し得る。

        6. それ以外で newSize が oldSize より小さい場合:

          1. stream の [[buffer]] を、stream の [[buffer]] の 先頭 newSize バイトを含む byte sequence に設定する。

        7. もし stream の [[seekOffset]] が newSize より大きいなら、 stream の [[seekOffset]] を newSize に設定する。

        8. Resolve p。

  4. p を返す。

2.5.1. write() メソッド

await stream . write(data)
await stream . write({ type: "write", data: data })

dataの内容をstreamに関連付けられたファイルの現在のカーソル位置に書き込みます。

ストリームが閉じられるまで、実際のディスク上のファイルには変更は反映されません。 変更は通常、一時ファイルに書き込まれます。

await stream . write({ type: "write", position: position, data: data })

dataの内容をstreamに関連付けられたファイルの先頭からpositionバイトの位置に書き込みます。 また、現在のファイルカーソル位置を書き込まれたデータの末尾に更新します。

ストリームが閉じられるまで、実際のディスク上のファイルには変更は反映されません。 変更は通常、一時ファイルに書き込まれます。

await stream . write({ type: "seek", position: position })

現在のファイルカーソル位置を、ファイルの先頭からpositionバイトの位置に更新します。

await stream . write({ type: "truncate", size: size })

streamに関連付けられたファイルをsizeバイトの長さにリサイズします。 もしsizeが現在のファイルサイズより大きい場合、ファイルはヌルバイトでパディングされます。 それ以外の場合、ファイルは切り詰められます。

truncate が呼び出されたとき、ファイルカーソルが更新されます。 カーソルがsizeより小さい場合はそのままです。 カーソルがsizeより大きい場合は、後続の書き込みがエラーにならないようsizeに設定されます。

ストリームが閉じられるまで、実際のファイルには変更は反映されません。 変更は通常、一時ファイルに書き込まれます。

The write(data) メソッド の手順は以下の通りです:
  1. writerを、ライターを取得することでthisに対して取得します。

  2. resultを、writerに対してチャンクを書き込む 結果として設定します。

  3. ライターを解放する writer。

  4. resultを返します。

2.5.2. seek() メソッド

await stream . seek(position)

現在のファイルカーソル位置を、ファイルの先頭からpositionバイトの位置に更新します。

The seek(position) メソッドの手順は以下の通りです:
  1. writerを、ライターを取得することでthisに対して取得します。

  2. resultを、writerに対してチャンクを書き込む 結果として設定します。 «[ "type" → "seek", "position" → position ]».

  3. ライターを解放する writer。

  4. resultを返します。

2.5.3. truncate() メソッド

await stream . truncate(size)

streamに関連付けられたファイルをsizeバイトの長さにリサイズします。sizeが現在のファイルサイズより大きい場合、ファイルはヌルバイトでパディングされます。それ以外の場合、ファイルは切り詰められます。

truncate が呼び出されたとき、ファイルカーソルが更新されます。カーソルがsizeより小さい場合はそのままです。カーソルがsizeより大きい場合は、後続の書き込みがエラーにならないようsizeに設定されます。

ストリームが閉じられるまで、実際のファイルには変更は反映されません。変更は通常、一時ファイルに書き込まれます。

The truncate(size) メソッドの手順は以下の通りです:
  1. writerを、ライターを取得することでthisに対して取得します。

  2. resultを、writerに対してチャンクを書き込む 結果として設定します。 «[ "type" → "truncate", "size" → size ]».

  3. ライターを解放する writer。

  4. resultを返します。

2.6. FileSystemSyncAccessHandle インターフェイス

dictionary FileSystemReadWriteOptions {
  [EnforceRange] unsigned long long at;
};

[Exposed=DedicatedWorker, SecureContext]
interface FileSystemSyncAccessHandle {
  unsigned long long read(AllowSharedBufferSource buffer,
                          optional FileSystemReadWriteOptions options = {});
  unsigned long long write(AllowSharedBufferSource buffer,
                           optional FileSystemReadWriteOptions options = {});

  undefined truncate([EnforceRange] unsigned long long newSize);
  unsigned long long getSize();
  undefined flush();
  undefined close();
};

FileSystemSyncAccessHandle には関連付けられた [[file]] (ファイルエントリ)があります。

FileSystemSyncAccessHandle には関連付けられた [[state]] があり、文字列 "open" または "closed" のいずれかのみを取ります。

FileSystemSyncAccessHandle は、単一のファイルに対して読み取り/書き込みを行い、そのサイズを取得および変更することができるオブジェクトです。

FileSystemSyncAccessHandle は同期メソッドを提供します。これにより、非同期操作に高いオーバーヘッドが伴うコンテキスト(例:WebAssembly)で高いパフォーマンスを実現できます。

FileSystemSyncAccessHandle には、ファイルの先頭からバイトオフセット0で初期化された ファイルポジションカーソル があります。

新しい FileSystemSyncAccessHandleを作成するには、 ファイルエントリー file が Realm realm 内で与えられた場合、以下を行う:
  1. handle を new された FileSystemSyncAccessHandle として realm 内で作成します。

  2. handle の [[file]] を file に設定します。

  3. handle の [[state]] を "open" に設定します。

  4. handle を返します。

2.6.1. read() メソッド

handle . read(buffer)
handle . read(buffer, { at })

handle に関連付けられたファイルの内容を buffer に読み込みます。必要に応じて、指定されたオフセットで読み込みます。

read() が呼び出されると、ファイルカーソルが最後に読み込まれたバイトの次のバイトを指すように更新されます。

外部で変更されたファイルを読み込む際に Access Handle がどのように動作すべきかを規定する。 [Issue #35]

The read(buffer, FileSystemReadWriteOptions: options) メソッドの手順は次の通り:
  1. this の [[state]] が "closed" であれば、 throw "InvalidStateError" DOMException を投げる。

  2. bufferSize を buffer の byte length とする。

  3. fileContents を this の [[file]] の binary data とする。

  4. fileSize を fileContents の length とする。

  5. readStart を options["at"] が exists するなら options["at"]、そうでなければ this の file position cursor とする。

  6. 基盤となるファイルシステムが readStart のファイルオフセットからの読み出しをサポートしない場合、 throw TypeError を投げる。

  7. もし readStart が fileSize より大きいなら:

    1. this の file position cursor を fileSize に設定する。

    2. 0 を返す。

  8. readEnd を readStart + (bufferSize − 1) とする。

  9. もし readEnd が fileSize より大きいなら、readEnd を fileSize に設定する。

  10. bytes を byte sequence で、fileContents の readStart から readEnd までの バイトを含むものとする。

  11. result を bytes の length とする。

  12. 直前の手順で fileContents からの読み取り操作が失敗した場合:

    1. 部分的な読み取りがあり、bytes に読み込まれたバイト数が分かるなら、 result をその読み取ったバイト数に設定する。

    2. そうでなければ result を 0 に設定する。

  13. arrayBuffer を buffer の underlying buffer とする。

  14. Write bytes を arrayBuffer に書き込む。

  15. this の file position cursor を readStart + result に設定する。

  16. result を返す。

2.6.2. write() メソッド

handle . write(buffer)
handle . write(buffer, { at })

handle に関連付けられたファイルに buffer の内容を書き込みます。必要に応じて指定されたオフセットから書き込みを行い、書き込まれたバイト数を返します。 返された書き込まれたバイト数を確認することで、呼び出し元はエラーや部分的な書き込みを検出して処理することができます。

write() が呼び出されると、ファイルカーソルが最後に書き込まれたバイトの次のバイトを指すように更新されます。

The write(buffer, FileSystemReadWriteOptions: options) メソッドの手順は次の通り:
  1. もし this の [[state]] が "closed" なら、 throw "InvalidStateError" DOMException を投げる。

  2. writePosition を options["at"] が exists するなら options["at"]、そうでなければ this の file position cursor とする。

  3. 基盤となるファイルシステムが writePosition のファイルオフセットへの書き込みをサポートしない場合、 throw TypeError を投げる。

  4. fileContents を this の [[file]] の binary data のコピーとする。

  5. oldSize を fileContents の length とする。

  6. bufferSize を buffer の byte length とする。

  7. もし writePosition が oldSize より大きいなら、 writePosition − oldSize 個の 0x00 (NUL) バイトを fileContents の末尾に付加する。

    Note: スキップされた領域が実際に NUL バイトで埋められているかのように実装は振る舞うことが期待される。 これはそれらのバイトが必ずしもディスクへ書き込まれ領域を消費することを意味しない。 多くのファイルシステムは NUL バイトが物理的領域を消費しないいわゆるスパースファイルをサポートしている。

  8. head を byte sequence で fileContents の先頭 writePosition バイトを含むものとする。

  9. tail を空の byte sequence とする。

  10. もし writePosition + bufferSize が oldSize より小さいなら:

    1. tail を byte sequence で、 fileContents の最後の oldSize − (writePosition + bufferSize) バイトを含むものに設定する。

  11. newSize を head の length + bufferSize + tail の length とする。

  12. もし newSize − oldSize が利用可能な storage quota を超えるなら、 throw QuotaExceededError を投げる。

  13. this の [[file]] の binary data を head、buffer の内容、および tail の連結に設定する。

    Note: buffer の内容へアクセスする機構は意図的に曖昧にしてある。 多くの実装は(buffer のコピーを作成する代わりに)ホスト OS への直接書き込みを行うことで性能を重視すると考えられ、 これにより書き込み順序や部分書き込み結果の詳細な仕様化を妨げる。

  14. 直前の手順で this の [[file]] の binary data を変更する操作が失敗した場合:

    1. 部分的な書き込みがあり、buffer から書き込まれたバイト数が判明しているなら:

      1. bytesWritten を buffer から書き込まれたバイト数とする。

      2. this の file position cursor を writePosition + bytesWritten に設定する。

      3. bytesWritten を返す。

    2. それ以外の場合 throw "InvalidStateError" DOMException を投げる。

  15. this の file position cursor を writePosition + bufferSize に設定する。

  16. bufferSize を返す。

2.6.3. truncate() メソッド

handle . truncate(newSize)

handle に関連付けられたファイルを newSize バイトの長さにリサイズします。newSize が現在のファイルサイズより大きい場合、ファイルはヌルバイトで埋められます。それ以外の場合、ファイルは切り詰められます。

truncate が呼び出されたとき、ファイルカーソルが更新されます。カーソルが newSize より小さい場合はそのままです。カーソルが newSize より大きい場合、newSize に設定されます。

truncate(newSize) メソッドの手順は次のとおり:
  1. this の [[state]] が "closed" の場合、 "throw" し、"InvalidStateError" DOMException を投げる。

  2. fileContents を this の [[file]] の binary data のコピーとする。

  3. oldSize を length(長さ)である this の [[file]] の binary data の長さとする。

  4. 基盤となるファイルシステムがファイルサイズを newSize に設定することをサポートしない場合、 "throw" し、TypeError を投げる。

  5. newSize が oldSize より大きい場合:

    1. newSize − oldSize が利用可能な storage quota を超えるなら、 "throw" し、QuotaExceededError を投げる。

    2. this の [[file]] を、 byte sequence(バイト列)であって fileContents と byte sequence(newSize − oldSize 個の 0x00 バイトを含む)を連結したものに設定する。

    3. 前のステップで this の [[file]] の binary data を変更する操作が失敗した場合、 "throw" し、"InvalidStateError" DOMException を投げる。

  6. それ以外で newSize が oldSize より小さい場合:

    1. this の [[file]] を、 byte sequence(fileContents の先頭 newSize バイトを含む)に設定する。

    2. 前のステップで this の [[file]] の binary data を変更する操作が失敗した場合、 "throw" し、"InvalidStateError" DOMException を投げる。

  7. this の file position cursor が newSize より大きいなら、file position cursor を newSize に設定する。

2.6.4. getSize() メソッド

handle . getSize()

handle に関連付けられたファイルのサイズをバイト数で返す。

The getSize() メソッドの手順は次のとおり:
  1. this の [[state]] が "closed" の場合、 throw し、"InvalidStateError" DOMException を投げる。

  2. this の [[file]] の binary data の length を返す。

2.6.5. flush() メソッド

handle . flush()

handle に関連付けられたファイルの内容が、write() によって行われたすべての変更を反映していることを保証する。

The flush() method steps are:
  1. this の [[state]] が "closed" の場合、 throw し、"InvalidStateError" DOMException を投げる。

  2. ファイル内容に対するすべてのキャッシュされた変更を、ファイルシステムの基盤となる記憶装置へ転送しようと試みる。

    Note: これはフラッシュ (flushing) とも呼ばれる。一部のインメモリファイルシステムのように、フラッシュ先となる「ディスク」を持たないファイルシステムでは no-op になる場合がある。

2.6.6. close() メソッド

handle . close()

アクセスハンドルを閉じるか、すでにアクセスハンドルが閉じられている場合は何もしません。これにより、以降の操作が無効になり、ロックが解除されます。handle に関連付けられた [[file]] の。

close() メソッドの手順は以下の通りです:
  1. もし this の [[state]] が "closed" の場合、終了します。

  2. this の [[state]] を "closed" に設定します。

  3. lockReleased を false に設定します。

  4. file を this の [[file]] に設定します。

  5. 以下のステップをエンキューして ファイルシステムキュー に追加します:

    1. ロックを解除します。file の。

    2. lockReleased を true に設定します。

  6. 一時停止 します。lockReleased が true になるまで。

注: このメソッドは、ファイルのすべての変更が基盤となるストレージデバイスに即座に反映されることを保証するものではありません。この保証が必要な場合は、flush() メソッドを先に呼び出してください。

3. バケットファイルシステムへのアクセス

バケットファイルシステムは、 ストレージエンドポイントであり、その 識別子は"fileSystem"、 タイプは« "local" »、 クォータはnullです。

ストレージエンドポイントは、[storage]自体で定義されるべきです。ここで定義するのではなく、そちらの表に統合してください。

注: ユーザーエージェントは通常、バケットファイルシステムの内容をディスクに永続化することでこれを実装しますが、内容が容易にユーザーがアクセス可能であることは意図されていません。同様に、バケットファイルシステムの子要素の名前と一致する名前を持つファイルやディレクトリが存在するという期待もありません。

[SecureContext]
partial interface StorageManager {
  Promise<FileSystemDirectoryHandle> getDirectory();
};
directoryHandle = await navigator . storage . getDirectory()

バケットファイルシステムのルートディレクトリを返します。

getDirectory() メソッドの手順は以下の通りです:
  1. environmentを、現在の設定オブジェクトとします。

  2. 次のパラメーターenvironmentおよび"fileSystem"を使用して、ローカルストレージボトルマップを取得を実行した結果をmapとします。これが失敗を返した場合、"SecurityError" DOMExceptionで拒否されたプロミスを返します。

  3. もしmap["root"]が存在しない場合:

    1. dirを、新しいディレクトリエントリとし、そのアクセスの照会および アクセスの要求アルゴリズムが常に ファイルシステムアクセス結果を返すものとします。 結果には、許可状態 "granted" と エラー名の空文字列を含むものとします。

    2. dirの名前を空文字列に設定します。

    3. dirの子要素を空の集合に設定します。

    4. map["root"]をdirに設定します。

  4. rootを実装依存の不透明な文字列とします。

  5. pathを« 空文字列 »とします。

  6. handleを、現在のRealmで rootとpathを用いて新しい FileSystemDirectoryHandleを作成する結果とします。

    注: rootには ストレージバケットなどの関連する識別情報が含まれる場合があります。

  7. 断言: エントリの位置特定をhandleのロケーターを与えて実行した結果は、 map["root"]と同じエントリである ディレクトリエントリを返します。

  8. 解決されたPromiseとしてhandleを返します。

謝辞

感謝の意を表します: Alex Danilo、 Anne van Kesteren、 Anoesj Sadraee、 Austin Sullivan、 Chase Phillips、 Daseul Lee、 Dru Knox、 Edgar Chen、 Emanuel Krivoy、 Hazim Mohamed、 Ingvar Stepanyan、 Jari Jalkanen、 Joshua Bell、 Kagami Sascha Rosylight、 Marcos Cáceres、 Martin Thomson、 Olivier Yiptong、 Philip Jägenstedt、 Randell Jesup、 Richard Stotz、 Ruth John、 Sid Vishnoi、 Sihui Liu、 Stefan Sauer、 Thomas Steiner、 Victor Costan、および Youenn Fablet、 彼らの素晴らしさに対して!

この標準は Marijn Kruisselbrink (Google, mek@chromium.org) によって執筆されました。

知的財産権

この現行標準には、W3C WICG の File System Access からコピーされた素材が含まれており、 これは W3C ソフトウェアおよびドキュメントライセンス の下で利用可能です。

Copyright © WHATWG (Apple, Google, Mozilla, Microsoft)。この作品は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下でライセンスされています。これの一部がソースコードに組み込まれている場合、その部分はBSD 3-Clause ライセンスの下でライセンスされています。

これは現行標準です。特許レビュー版に興味のある方は、現行標準レビュー版をご覧ください。

索引

この仕様書で定義される用語

参照によって定義される用語

参考文献

規範的参照

[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[ENCODING]
Anne van Kesteren. Encoding Standard. 現行標準. URL: https://encoding.spec.whatwg.org/
[FILE-API]
Marijn Kruisselbrink. File API. URL: https://w3c.github.io/FileAPI/
[HTML]
Anne van Kesteren; et al. HTML Standard. 現行標準. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. 現行標準. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[PERMISSIONS-REQUEST]
Requesting Permissions. Draft Community Group Report. URL: https://wicg.github.io/permissions-request/
[STORAGE]
Anne van Kesteren. Storage Standard. 現行標準. URL: https://storage.spec.whatwg.org/
[STREAMS]
Adam Rice; et al. Streams Standard. 現行標準. URL: https://streams.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. 現行標準. URL: https://webidl.spec.whatwg.org/

IDL 索引

enum FileSystemHandleKind {
  "file",
  "directory",
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemHandle {
  readonly attribute FileSystemHandleKind kind;
  readonly attribute USVString name;

  Promise<boolean> isSameEntry(FileSystemHandle other);
};

dictionary FileSystemCreateWritableOptions {
  boolean keepExistingData = false;
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemFileHandle : FileSystemHandle {
  Promise<File> getFile();
  Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWritableOptions options = {});
  [Exposed=DedicatedWorker]
  Promise<FileSystemSyncAccessHandle> createSyncAccessHandle();
};

dictionary FileSystemGetFileOptions {
  boolean create = false;
};

dictionary FileSystemGetDirectoryOptions {
  boolean create = false;
};

dictionary FileSystemRemoveOptions {
  boolean recursive = false;
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemDirectoryHandle : FileSystemHandle {
  async_iterable<USVString, FileSystemHandle>;

  Promise<FileSystemFileHandle> getFileHandle(USVString name, optional FileSystemGetFileOptions options = {});
  Promise<FileSystemDirectoryHandle> getDirectoryHandle(USVString name, optional FileSystemGetDirectoryOptions options = {});

  Promise<undefined> removeEntry(USVString name, optional FileSystemRemoveOptions options = {});

  Promise<sequence<USVString>?> resolve(FileSystemHandle possibleDescendant);
};

enum WriteCommandType {
  "write",
  "seek",
  "truncate",
};

dictionary WriteParams {
  required WriteCommandType type;
  unsigned long long? size;
  unsigned long long? position;
  (BufferSource or Blob or USVString)? data;
};

typedef (BufferSource or Blob or USVString or WriteParams) FileSystemWriteChunkType;

[Exposed=(Window,Worker), SecureContext]
interface FileSystemWritableFileStream : WritableStream {
  Promise<undefined> write(FileSystemWriteChunkType data);
  Promise<undefined> seek(unsigned long long position);
  Promise<undefined> truncate(unsigned long long size);
};

dictionary FileSystemReadWriteOptions {
  [EnforceRange] unsigned long long at;
};

[Exposed=DedicatedWorker, SecureContext]
interface FileSystemSyncAccessHandle {
  unsigned long long read(AllowSharedBufferSource buffer,
                          optional FileSystemReadWriteOptions options = {});
  unsigned long long write(AllowSharedBufferSource buffer,
                           optional FileSystemReadWriteOptions options = {});

  undefined truncate([EnforceRange] unsigned long long newSize);
  unsigned long long getSize();
  undefined flush();
  undefined close();
};


[SecureContext]
partial interface StorageManager {
  Promise<FileSystemDirectoryHandle> getDirectory();
};