File System Access API: ローカル ファイルへのアクセスを簡素化

File System Access API を使用すると、ユーザーのデバイス上のファイルやフォルダの変更をウェブアプリから直接読み取り、保存できます。

File System Access API とは

File System Access API(旧称: Native File System API、旧称 Writeable Files API)を使用すると、デベロッパーは、IDE、写真と動画のエディタ、テキスト エディタなど、ユーザーのローカル デバイス上のファイルとやり取りする強力なウェブアプリを構築できます。ユーザーがウェブアプリにアクセス権を付与すると、ユーザーはこの API を使用して、ユーザーのデバイス上のファイルとフォルダに対する変更を直接読み込んだり、保存したりできます。File System Access API は、ファイルの読み取りと書き込みだけでなく、ディレクトリを開いてそのコンテンツを列挙することもできます。

これまでにファイルの読み取りと書き込みに携わったことがあれば、これから説明する内容の多くはおなじみのものでしょう。システムによっては一様ではありませんので、とにかくお読みいただくことをおすすめします。

File System Access API は現在、Windows、macOS、ChromeOS、Linux 上のほとんどの Chromium ブラウザでサポートされています。顕著な例外は Brave です。現在はフラグの背後でしか利用できません。Android は、Chromium 109 以降、API のオリジン プライベート ファイル システムの部分をサポートしています。現在、選択ツールメソッドに関する計画はありませんが、crbug.com/1011535 にスターを付けることで今後の進捗状況を追跡できます。

File System Access API の使用

File System Access API のパワーと有用性を示すために、1 つのファイルのテキスト エディタを作成しました。テキスト ファイルを開いて編集したり、変更内容をディスクに保存したり、新しいファイルを開始してディスクに変更を保存したりできます。複雑な内容ではありませんが、コンセプトの理解には十分です。

ブラウザ サポート

対応ブラウザ

  • 86
  • 86
  • x
  • x

ソース

試してみる

テキスト エディタのデモで File System Access API の実際の動作をご覧ください。

ローカル ファイル システムからファイルを読み取る

まず取り組むユースケースは、ユーザーにファイルの選択を依頼し、そのファイルを開いてディスクから読み取ることです。

読み上げるファイルを選択するようユーザーに依頼する

File System Access API のエントリ ポイントは window.showOpenFilePicker() です。呼び出すと、ファイル選択ツールのダイアログ ボックスが表示され、ユーザーにファイルの選択を求めるメッセージが表示されます。ファイルを選択すると、API はファイル ハンドルの配列を返します。オプションの options パラメータを使用すると、ユーザーが複数のファイルやディレクトリ、ファイル形式を選択できるようになるなど、ファイル選択ツールの動作に影響を与えることができます。オプションを指定しない場合、ユーザーはファイル選択ツールで 1 つのファイルを選択できます。これはテキスト エディタに最適です。

他の多くの強力な API と同様に、showOpenFilePicker() の呼び出しは安全なコンテキストで行い、ユーザー操作内から呼び出す必要があります。

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  // Destructure the one-element array.
  [fileHandle] = await window.showOpenFilePicker();
  // Do something with the file handle.
});

ユーザーがファイルを選択すると、showOpenFilePicker() はハンドルの配列を返します。この場合は、ファイルの操作に必要なプロパティとメソッドを含む 1 つの FileSystemFileHandle を含む 1 要素配列です。

後で使用できるように、ファイル ハンドルへの参照を保持しておくと便利です。ファイルに変更を保存したり、その他のファイル操作を実行したりするために必要です。

ファイル システムからファイルを読み取る

これで、ファイルに対するハンドルが取得できたので、ファイルのプロパティを取得するか、ファイル自体にアクセスできます。ここでは、その内容を読み上げます。handle.getFile() を呼び出すと、blob を含む File オブジェクトが返されます。blob からデータを取得するには、そのメソッドslice()stream()text()arrayBuffer())のいずれかを呼び出します。

const file = await fileHandle.getFile();
const contents = await file.text();

FileSystemFileHandle.getFile() から返される File オブジェクトは、ディスク上の基盤となるファイルが変更されていない限り、読み取り可能です。ディスク上のファイルが変更されると、File オブジェクトは読み取り不能になり、getFile() を再度呼び出して、変更されたデータを読み取る新しい File オブジェクトを取得する必要があります。

すべてを組み合わせる

ユーザーが [開く] ボタンをクリックすると、ブラウザにファイル選択ツールが表示されます。ファイルを選択すると、アプリはコンテンツを読み取り、<textarea> に格納します。

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  [fileHandle] = await window.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.text();
  textArea.value = contents;
});

ファイルをローカル ファイル システムに書き込む

テキスト エディタでファイルを保存するには、[保存] と [名前を付けて保存] の 2 つの方法があります。[Save] は、前に取得したファイル ハンドルを使用して、変更を元のファイルに戻します。ただし、[名前を付けて保存] では新しいファイルが作成されるため、新しいファイル ハンドルが必要になります。

新しいファイルを作成する

ファイルを保存するには、showSaveFilePicker() を呼び出します。これにより、ファイル選択ツールが「保存」モードで表示され、ユーザーが保存に使用する新しいファイルを選択できるようになります。また、テキスト エディタの場合、.txt 拡張機能を自動的に追加するように、追加のパラメータをいくつか指定しました。

async function getNewFileHandle() {
  const options = {
    types: [
      {
        description: 'Text Files',
        accept: {
          'text/plain': ['.txt'],
        },
      },
    ],
  };
  const handle = await window.showSaveFilePicker(options);
  return handle;
}

ディスクへの変更を保存

変更をファイルに保存するためのコードはすべて、GitHubテキスト エディタのデモで確認できます。コアファイル システムの操作は fs-helpers.js にあります。プロセスを簡単に表すと、以下のようになります。各ステップを順を追って説明します。

// fileHandle is an instance of FileSystemFileHandle..
async function writeFile(fileHandle, contents) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Write the contents of the file to the stream.
  await writable.write(contents);
  // Close the file and write the contents to disk.
  await writable.close();
}

ディスクへのデータの書き込みには、WritableStream のサブクラスである FileSystemWritableFileStream オブジェクトを使用します。ファイル ハンドル オブジェクトで createWritable() を呼び出して、ストリームを作成します。createWritable() が呼び出されると、ブラウザはまず、ユーザーがファイルへの書き込み権限を付与したかどうかを確認します。書き込み権限が付与されていない場合、ブラウザはユーザーに権限の付与を求めます。権限が付与されていない場合、createWritable()DOMException をスローし、アプリはファイルに書き込むことができません。テキスト エディタでは、DOMException オブジェクトは saveFile() メソッドで処理されます。

write() メソッドは、テキスト エディタに必要な文字列を受け取ります。ただし、BufferSource または Blob を受け取ることもできます。たとえば、ストリームをそのストリームに直接パイプできます。

async function writeURLToFile(fileHandle, url) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Make an HTTP request for the contents.
  const response = await fetch(url);
  // Stream the response into the file.
  await response.body.pipeTo(writable);
  // pipeTo() closes the destination pipe by default, no need to close it.
}

ストリーム内で seek() または truncate() を使用して、特定の位置のファイルを更新したり、ファイルのサイズを変更したりできます。

推奨されるファイル名と開始ディレクトリを指定する

多くの場合、アプリでデフォルトのファイル名や場所の候補を表示させたいことがあります。たとえば、テキスト エディタでデフォルトのファイル名を Untitled ではなく Untitled Text.txt にするよう提案する場合があります。そのためには、showSaveFilePicker オプションの一部として suggestedName プロパティを渡します。

const fileHandle = await self.showSaveFilePicker({
  suggestedName: 'Untitled Text.txt',
  types: [{
    description: 'Text documents',
    accept: {
      'text/plain': ['.txt'],
    },
  }],
});

デフォルトの開始ディレクトリについても同様です。テキスト エディタを作成する場合は、ファイルの保存や開くダイアログをデフォルトの documents フォルダから開始することをおすすめします。画像エディタの場合は、デフォルトの pictures フォルダから開始することをおすすめします。デフォルトの開始ディレクトリを提案するには、次のように startIn プロパティを showSaveFilePickershowDirectoryPicker()、または showOpenFilePicker のメソッドに渡します。

const fileHandle = await self.showOpenFilePicker({
  startIn: 'pictures'
});

よく知られているシステム ディレクトリは次のとおりです。

  • desktop: ユーザーのデスクトップ ディレクトリ(存在する場合)。
  • documents: ユーザーが作成したドキュメントが通常保存されるディレクトリ。
  • downloads: ダウンロードしたファイルが通常保存されるディレクトリ。
  • music: 音声ファイルが通常保存されるディレクトリ。
  • pictures: 写真などの静止画像が通常保存されるディレクトリ。
  • videos: 動画/映画が通常保存されるディレクトリ。

既知のシステム ディレクトリとは別に、既存のファイルまたはディレクトリ ハンドルを startIn の値として渡すこともできます。ダイアログが同じディレクトリで開きます。

// Assume `directoryHandle` is a handle to a previously opened directory.
const fileHandle = await self.showOpenFilePicker({
  startIn: directoryHandle
});

さまざまなファイル選択ツールの目的を指定する

アプリケーションによっては、目的に応じて選択ツールが異なることがあります。たとえば、リッチテキスト エディタを使用すると、テキスト ファイルを開くだけでなく、画像をインポートすることもできます。デフォルトでは、各ファイル選択ツールは最後に記憶された場所で開きます。これを回避するには、選択ツールのタイプごとに id 値を保存します。id が指定された場合、ファイル選択ツールの実装には、その id に対して最後に使用されたディレクトリが個別に記憶されます。

const fileHandle1 = await self.showSaveFilePicker({
  id: 'openText',
});

const fileHandle2 = await self.showSaveFilePicker({
  id: 'importImage',
});

IndexedDB へのファイル ハンドルまたはディレクトリ ハンドルの保存

ファイル ハンドルとディレクトリ ハンドルはシリアル化可能です。つまり、ファイルまたはディレクトリ ハンドルを IndexedDB に保存したり、postMessage() を呼び出して同じトップレベルのオリジン間で送信したりできます。

ファイルまたはディレクトリのハンドルを IndexedDB に保存すると、状態を保存したり、ユーザーが作業していたファイルやディレクトリを覚えたりできます。これにより、最近開いたファイルや編集したファイルのリストを保持したり、アプリを開いたときに最後のファイルを再度開くよう提案したり、以前の作業ディレクトリを復元したりできます。テキスト エディタでは、ユーザーが最近開いた 5 つのファイルのリストを保存し、それらのファイルに再度アクセスしやすくしています。

以下のコードサンプルは、ファイル ハンドルとディレクトリ ハンドルの保存と取得を示しています。Glitch で実際の動作を確認できます。(簡略化のため idb-keyval ライブラリを使用しています)。

import { get, set } from 'https://unpkg.com/idb-keyval@5.0.2/dist/esm/index.js';

const pre1 = document.querySelector('pre.file');
const pre2 = document.querySelector('pre.directory');
const button1 = document.querySelector('button.file');
const button2 = document.querySelector('button.directory');

// File handle
button1.addEventListener('click', async () => {
  try {
    const fileHandleOrUndefined = await get('file');
    if (fileHandleOrUndefined) {
      pre1.textContent = `Retrieved file handle "${fileHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const [fileHandle] = await window.showOpenFilePicker();
    await set('file', fileHandle);
    pre1.textContent = `Stored file handle for "${fileHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

// Directory handle
button2.addEventListener('click', async () => {
  try {
    const directoryHandleOrUndefined = await get('directory');
    if (directoryHandleOrUndefined) {
      pre2.textContent = `Retrieved directroy handle "${directoryHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const directoryHandle = await window.showDirectoryPicker();
    await set('directory', directoryHandle);
    pre2.textContent = `Stored directory handle for "${directoryHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

保存されたファイルまたはディレクトリのハンドルと権限

現在、権限はセッション間で保持されないため、queryPermission() を使用してユーザーがファイルまたはディレクトリに権限を付与したことを確認する必要があります。まだリクエストされていない場合は、requestPermission() を呼び出して(再)リクエストします。これは、ファイルハンドルとディレクトリハンドルでも同じように機能します。fileOrDirectoryHandle.requestPermission(descriptor) または fileOrDirectoryHandle.queryPermission(descriptor) をそれぞれ実行する必要があります。

テキスト エディタで、ユーザーがすでに権限を付与しているかどうかを確認し、必要に応じてリクエストを行う verifyPermission() メソッドを作成しました。

async function verifyPermission(fileHandle, readWrite) {
  const options = {};
  if (readWrite) {
    options.mode = 'readwrite';
  }
  // Check if permission was already granted. If so, return true.
  if ((await fileHandle.queryPermission(options)) === 'granted') {
    return true;
  }
  // Request permission. If the user grants permission, return true.
  if ((await fileHandle.requestPermission(options)) === 'granted') {
    return true;
  }
  // The user didn't grant permission, so return false.
  return false;
}

読み取りリクエストで書き込み権限をリクエストすることで、権限プロンプトの数を減らしました。ファイルを開くと 1 つのプロンプトが表示され、読み取り権限と書き込み権限の両方が付与されます。

ディレクトリを開いて内容を列挙する

ディレクトリ内のすべてのファイルを列挙するには、showDirectoryPicker() を呼び出します。ユーザーが選択ツールでディレクトリを選択すると、FileSystemDirectoryHandle が返されます。これにより、ディレクトリのファイルを列挙してアクセスできます。デフォルトでは、ディレクトリ内のファイルへの読み取りアクセス権がありますが、書き込みアクセス権が必要な場合は、メソッドに { mode: 'readwrite' } を渡すことができます。

const butDir = document.getElementById('butDirectory');
butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  for await (const entry of dirHandle.values()) {
    console.log(entry.kind, entry.name);
  }
});

さらに、個々のファイルサイズを取得する目的で getFile() を介して各ファイルにアクセスする必要がある場合は、各結果に対して await を順次使用するのではなく、Promise.all() などを使用してすべてのファイルを並行して処理します。

const butDir = document.getElementById('butDirectory');
butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  const promises = [];
  for await (const entry of dirHandle.values()) {
    if (entry.kind !== 'file') {
      continue;
    }
    promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`));
  }
  console.log(await Promise.all(promises));
});

ディレクトリ内のファイルとフォルダの作成またはアクセス

ディレクトリから、getFileHandle() または getDirectoryHandle() メソッドを使用してファイルやフォルダにアクセスしたり、ファイルとフォルダにアクセスしたりできます。キーが create、ブール値が true または false であるオプションの options オブジェクトを渡すことで、ファイルまたはフォルダが存在しない場合に新規に作成する必要があるかどうかを判断できます。

// In an existing directory, create a new directory named "My Documents".
const newDirectoryHandle = await existingDirectoryHandle.getDirectoryHandle('My Documents', {
  create: true,
});
// In this new directory, create a file named "My Notes.txt".
const newFileHandle = await newDirectoryHandle.getFileHandle('My Notes.txt', { create: true });

ディレクトリ内のアイテムのパスを解決する

ディレクトリ内のファイルやフォルダを操作する場合、問題のアイテムのパスを解決すると便利です。そのためには、その名前がちょうどよい resolve() メソッドを使用します。解決する場合、アイテムはディレクトリの直接または間接の子にできます。

// Resolve the path of the previously created file called "My Notes.txt".
const path = await newDirectoryHandle.resolve(newFileHandle);
// `path` is now ["My Documents", "My Notes.txt"]

ディレクトリ内のファイルとフォルダの削除

ディレクトリへのアクセス権を取得した場合は、そのファイルとフォルダを removeEntry() メソッドを使用して削除できます。フォルダの場合、必要に応じて再帰的に削除でき、すべてのサブフォルダとその中にあるファイルを削除できます。

// Delete a file.
await directoryHandle.removeEntry('Abandoned Projects.txt');
// Recursively delete a folder.
await directoryHandle.removeEntry('Old Stuff', { recursive: true });

ファイルまたはフォルダの直接削除

ファイルまたはディレクトリのハンドルにアクセスできる場合は、FileSystemFileHandle または FileSystemDirectoryHandle に対して remove() を呼び出して削除します。

// Delete a file.
await fileHandle.remove();
// Delete a directory.
await directoryHandle.remove();

ファイルやフォルダの名前の変更と移動

ファイルやフォルダの名前を変更したり、新しい場所に移動したりするには、FileSystemHandle インターフェースの move() を呼び出します。FileSystemHandle には、子インターフェース FileSystemFileHandleFileSystemDirectoryHandle があります。move() メソッドは 1 つまたは 2 つのパラメータを受け取ります。1 つ目は、新しい名前の文字列か、宛先フォルダへの FileSystemDirectoryHandle です。後者の場合、オプションの 2 番目のパラメータは新しい名前の文字列になるため、移動と名前の変更を 1 ステップで行うことができます。

// Rename the file.
await file.move('new_name');
// Move the file to a new directory.
await file.move(directory);
// Move the file to a new directory and rename it.
await file.move(directory, 'newer_name');

ドラッグ&ドロップ統合

HTML ドラッグ&ドロップ インターフェースを使用すると、ウェブ アプリケーションはウェブページ上のドラッグ&ドロップ ファイルを受け入れることができます。ドラッグ&ドロップ オペレーション中は、ドラッグされたファイルとディレクトリのアイテムがそれぞれファイル エントリとディレクトリ エントリに関連付けられます。DataTransferItem.getAsFileSystemHandle() メソッドは、ドラッグされたアイテムがファイルの場合は FileSystemFileHandle オブジェクトを含む Promise を返し、ドラッグされたアイテムがディレクトリの場合は FileSystemDirectoryHandle オブジェクトを含む Promise を返します。以下に、実際の例を示します。ドラッグ&ドロップ インターフェースの DataTransferItem.kind は、ファイルとディレクトリの両方に対して "file" ですが、File System Access API の FileSystemHandle.kind はファイルに対して "file"、ディレクトリに対しては "directory" です。

elem.addEventListener('dragover', (e) => {
  // Prevent navigation.
  e.preventDefault();
});

elem.addEventListener('drop', async (e) => {
  e.preventDefault();

  const fileHandlesPromises = [...e.dataTransfer.items]
    .filter((item) => item.kind === 'file')
    .map((item) => item.getAsFileSystemHandle());

  for await (const handle of fileHandlesPromises) {
    if (handle.kind === 'directory') {
      console.log(`Directory: ${handle.name}`);
    } else {
      console.log(`File: ${handle.name}`);
    }
  }
});

送信元のプライベート ファイル システムへのアクセス

送信元の限定公開ファイル システムは、名前が示すように、ページの送信元に限定公開されるストレージ エンドポイントです。ブラウザは通常、このオリジンの非公開ファイル システムのコンテンツをどこかディスクに保持することでこれを実装しますが、コンテンツに簡単にアクセスできるようにすることは想定されていません。同様に、送信元のプライベート ファイル システムの子の名前と一致する名前のファイルやディレクトリが存在することは想定されません。ブラウザはファイルがあるように見えますが、内部的には(オリジンの非公開ファイル システムであるため)、ブラウザはこれらの「ファイル」をデータベースやその他のデータ構造に保存している場合があります。基本的に、この API を使用する場合、ハードディスク上のどこかで 1 対 1 で一致する作成ファイルが見つかるとは限りません。ルート FileSystemDirectoryHandle にアクセスできるようになると、送信元の非公開ファイル システムで通常どおり操作できるようになります。

const root = await navigator.storage.getDirectory();
// Create a new file handle.
const fileHandle = await root.getFileHandle('Untitled.txt', { create: true });
// Create a new directory handle.
const dirHandle = await root.getDirectoryHandle('New Folder', { create: true });
// Recursively remove a directory.
await root.removeEntry('Old Stuff', { recursive: true });

対応ブラウザ

  • 86
  • 86
  • 111
  • 15.2

ソース

元のプライベート ファイル システムからパフォーマンスが最適化されたファイルにアクセスする

送信元の非公開ファイル システムは、たとえばファイル コンテンツに対するインプレースの排他的な書き込みアクセス権を付与するなど、パフォーマンスが最適化された特殊なファイルへのオプション アクセスを提供します。Chromium 102 以降では、ファイル アクセスを簡素化するための追加メソッド createSyncAccessHandle() がオリジンのプライベート ファイル システムで利用できます(createSyncAccessHandle()(同期読み取り / 書き込みオペレーション用))。FileSystemFileHandle で公開されますが、ウェブワーカーでのみ公開されます。

// (Read and write operations are synchronous,
// but obtaining the handle is asynchronous.)
// Synchronous access exclusively in Worker contexts.
const accessHandle = await fileHandle.createSyncAccessHandle();
const writtenBytes = accessHandle.write(buffer);
const readBytes = accessHandle.read(buffer, { at: 1 });

ポリフィル

File System Access API メソッドを完全にポリフィルすることはできません。

  • showOpenFilePicker() メソッドは、<input type="file"> 要素で近似できます。
  • showSaveFilePicker() メソッドは <a download="file_name"> 要素でシミュレートできますが、これによってプログラムによるダウンロードがトリガーされ、既存のファイルを上書きすることはできません。
  • showDirectoryPicker() メソッドは、非標準の <input type="file" webkitdirectory> 要素を使用して多少エミュレートできます。

Google では browser-fs-access というライブラリを開発しました。このライブラリは可能な限り File System Access API を使用し、それ以外の場合は常に次善のオプションにフォールバックします。

セキュリティと権限

Chrome チームは、強力なウェブ プラットフォーム機能へのアクセスの制御で定義されている基本原則(ユーザーの制御と透明性、ユーザーのエルゴノミクスなど)を使用して、File System Access API を設計、実装しています。

ファイルを開く、新しいファイルを保存するとき

読み込むファイルを開くファイル選択ツール
既存のファイルを読み込むために使用するファイル選択ツール。

ファイルを開く際、ユーザーはファイル選択ツールを使ってファイルまたはディレクトリの読み取り権限を付与します。オープン ファイル選択ツールは、安全なコンテキストから提供される場合にのみ、ユーザー操作によってのみ表示できます。ユーザーが気が変わった場合は、ファイル選択ツールで選択をキャンセルでき、サイトは何もアクセスできなくなります。これは <input type="file"> 要素と同じ動作です。

ファイルをディスクに保存するファイル選択ツール。
ファイルをディスクに保存するために使用するファイル選択ツール。

同様に、ウェブアプリで新しいファイルを保存する場合、ブラウザには保存ファイル選択ツールが表示され、ユーザーは新しいファイルの名前と場所を指定できます。既存のファイルを上書きするのではなく、新しいファイルをデバイスに保存するため、ファイル選択ツールはファイルへの書き込み権限をアプリに付与します。

制限付きフォルダ

ユーザーとユーザーのデータを保護するため、ブラウザは特定のフォルダ(Windows などのコア オペレーティング システムのフォルダや macOS ライブラリのフォルダなど)への保存をユーザーに制限する場合があります。この場合、ブラウザにプロンプトが表示され、別のフォルダを選択するよう求められます。

既存のファイルまたはディレクトリを変更する

ウェブアプリは、ユーザーから明示的な許可を得ずにディスク上のファイルを変更できません。

権限プロンプト

以前に読み取りアクセス権を付与したファイルに対する変更を保存すると、ブラウザに権限プロンプトが表示され、サイトがディスクに変更を書き込むための権限をリクエストします。権限リクエストは、保存ボタンのクリックなどのユーザー操作によってのみトリガーできます。

ファイルを保存する前に表示される権限プロンプト。
既存のファイルへの書き込み権限がブラウザに付与される前にユーザーに表示されるプロンプト。

また、IDE などで複数のファイルを編集するウェブアプリの場合は、起動時に変更を保存する権限をリクエストすることもできます。

ユーザーが [キャンセル] を選択して書き込みアクセス権を付与しなかった場合、ウェブアプリはローカル ファイルに変更を保存できません。ファイルを「ダウンロード」する方法、データをクラウドに保存する方法など、ユーザーがデータを保存するための代替方法を提供する必要があります。

透明性

アドレスバーのアイコン
ユーザーがウェブサイトにローカル ファイルへの保存権限を付与したことを示すアドレスバー アイコン。

ユーザーがウェブアプリにローカル ファイルを保存する権限を付与すると、ブラウザの URL バーにアイコンが表示されます。このアイコンをクリックすると、ポップオーバーが開き、ユーザーがアクセスを許可したファイルのリストが表示されます。ユーザーは必要に応じて簡単にアクセス権を取り消すことができます。

権限の永続性

ウェブアプリは、元のタブをすべて閉じるまで、プロンプトを表示せずにファイルに変更を保存できます。タブを閉じると、サイトはすべてのアクセス権を失います。次回ユーザーがウェブアプリを使用するときに、ファイルへのアクセスを求めるメッセージが再度表示されます。

フィードバック

File System Access API の使用体験をお聞かせください。

API の設計についてお聞かせください

API に想定したとおりに動作しない点はありますか。あるいは、アイデアを実装するために必要なメソッドやプロパティが欠落していないか?セキュリティ モデルについてご質問やご意見がある場合は、

実装に問題がある場合

Chrome の実装にバグが見つかりましたか?それとも、実装が仕様と異なりますか?

  • https://new.crbug.com でバグを報告します。できる限り詳細な情報と再現手順を記載し、[Components] を Blink>Storage>FileSystem に設定します。Glitch を使えば、再現をすばやく簡単に共有できます。

API を使用する予定がある場合は、

サイトで File System Access API を使用する予定がある場合は、皆様からのご支援のおかげで、Google が機能に優先順位を付け、そのサポートがいかに重要であるかを他のブラウザ ベンダーに示すことができます。

関連情報

謝辞

File System Access API の仕様は Marijn Kruisselbrink によって作成されました。