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

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

公開日: 2024 年 8 月 19 日

File System Access API を使用すると、IDE、写真や動画の編集ツール、テキスト エディタなど、ユーザーのローカル デバイス上のファイルを操作する強力なウェブアプリを構築できます。ユーザーがウェブアプリにアクセス権を付与すると、この API を使用して、ユーザーのデバイス上のファイルやフォルダに対する変更を直接読み取ったり保存したりできます。File System Access API は、ファイルの読み取りと書き込みだけでなく、ディレクトリを開いてその内容を列挙する機能も提供します。

ファイルの読み取りと書き込みを扱ったことがある方には、これから説明する内容の多くは馴染みのあるものだと思います。すべてのシステムが同じであるとは限らないため、ぜひお読みください。

FileSystem Access API は、Windows、macOS、ChromeOS、Linux、Android のほとんどの Chromium ブラウザでサポートされています。例外として、Brave では現在フラグの背後でのみ利用可能です。

File System Access API の使用

File System Access API の機能と有用性を紹介するために、単一ファイルのテキスト エディタを作成しました。テキスト ファイルを開いて編集したり、変更をディスクに保存したり、新しいファイルを作成して変更をディスクに保存したりできます。凝ったものではありませんが、コンセプトを理解するのに十分な情報を提供します。

ブラウザ サポート

Browser Support

  • Chrome: 86.
  • Edge: 86.
  • Firefox: not supported.
  • Safari: not supported.

Source

特徴検出

File System Access API がサポートされているかどうかを確認するには、目的のピッカー メソッドが存在するかどうかを確認します。

if ('showOpenFilePicker' in self) {
  // The `showOpenFilePicker()` method of the File System Access API is supported.
}

試してみる

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 オブジェクトが読み取り不能になり、変更されたデータを読み取るために新しい File オブジェクトを取得する getFile() を再度呼び出す必要があります。

すべてを組み合わせる

ユーザーが [開く] ボタンをクリックすると、ブラウザにファイル選択ツールが表示されます。ファイルを選択すると、アプリがその内容を読み取って <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 フォルダで開始したいでしょう。次のように、showSaveFilePickershowDirectoryPicker()showOpenFilePicker メソッドに startIn プロパティを渡すことで、デフォルトの開始ディレクトリを提案できます。

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' } を渡すことができます。

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() などを利用してすべてのファイルを並行して処理します。

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 または FileSystemDirectoryHandleremove() を呼び出して削除します。

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

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

ファイルとフォルダの名前を変更したり、新しい場所に移動したりするには、FileSystemHandle インターフェースで move() を呼び出します。FileSystemHandle には、子インターフェース FileSystemFileHandleFileSystemDirectoryHandle があります。move() メソッドは、1 つまたは 2 つのパラメータを取ります。最初の引数には、新しい名前の文字列または宛先フォルダの 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 });

Browser Support

  • Chrome: 86.
  • Edge: 86.
  • Firefox: 111.
  • Safari: 15.2.

Source

パフォーマンス向けに最適化されたファイルにオリジン プライベート ファイル システムからアクセスする

オリジン プライベート ファイル システムは、パフォーマンス向けに高度に最適化された特別な種類のファイルへのアクセスをオプションで提供します。たとえば、ファイルのコンテンツへのインプレースの排他的書き込みアクセスを提供します。Chromium 102 以降では、ファイル アクセスを簡素化するためのオリジン プライベート ファイル システムのメソッドが追加されています(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 は、可能な限り File System Access API を使用し、それ以外の場合は次に最適なオプションにフォールバックする browser-fs-access というライブラリを開発しました。

セキュリティと権限

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

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

読み取り用のファイルを開くファイル選択ツール
読み取り用に既存のファイルを開くために使用されるファイル選択ツール。

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

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

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

制限付きフォルダ

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

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

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

権限のプロンプト

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

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

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

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

透明性

アドレスバーのアイコン
ウェブサイトがローカル ファイルへの保存を許可されていることを示すアドレスバーのアイコン。

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

権限の永続性

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

フィードバック

File System Access API の使用感について、ぜひご意見をお聞かせください。

API 設計について教えてください

API が想定どおりに動作しない点はありますか?アイデアを実装するために必要なメソッドやプロパティが不足している場合はどうすればよいですか?セキュリティ モデルについて質問やコメントがある場合

実装に関する問題ですか?

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

  • https://new.crbug.com でバグを報告します。できるだけ詳細な情報と再現手順を記載し、[コンポーネント] を Blink>Storage>FileSystem に設定してください。

API を使用する予定ですか?

サイトで File System Access API を使用する予定ですか?公開でサポートしていただくことで、Google は機能の優先順位を決めやすくなり、他のブラウザ ベンダーにサポートの重要性を示すことができます。

関連情報

謝辞

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