【JavaScript】File System Access API – ファイルピッカーを表示して選択ファイルを読み取る方法 showOpenFilePicker() 編

ファイルピッカーを表示し、ユーザーに PC やスマートフォン内のファイルを選択させ、選択されたファイルの中身を読み取るには 2 つの方法があります。今回は、旧来から使われてきた <input type="file"> を使う方法と、モダンな showOpenFilePicker() を使った方法をそれぞれ解説し、それらの違いについて解説していきます。

<input type=”file”> を使う方法

PC やスマートフォン内のファイルを読み取るために以前より使われてきた <input type="file"> を使う方法について、おさらいしましょう。まずは HTML のマークアップをご覧ください。

<input type="file" id="file-input" accept=".txt">

ファイルをユーザーに選択させるために type 属性に "file" をセットした input 要素を用意します。この input 要素に accept 属性を使うと、ファイルピッカーに表示するファイルの種類をファイル拡張子でフィルターすることができます。ここでは .txt に限定しています。accept 属性には MIME タイプを指定することも可能です。

<input type="file" id="file-input" accept="text/plain">

MacOS ではファイル拡張子が存在しない場合がありますので、accept 属性にはファイル拡張子と MIME タイプの両方を指定するのが良いでしょう。複数の条件を指定するには半角カンマで区切ります。

<input type="file" id="file-input" accept=".txt,text/plain">
const file_input = document.getElementById('file-input');
file_input.addEventListener('change', async () => {
  const reader = new FileReader();
  reader.readAsText(file_input.files[0], 'UTF-8');
  reader.onload = (event) => {
    console.log(event.target.result);
  };
});

<input type="file"> を使ううえで大きなデメリットといえば、コーントロールのデザインや表記の自由度がないという点でしょう。これを解決するために、CSS で input 要素を非表示にして、label 要素を使って見た目をアレンジするというハック的な方法が使われてきました。

<div class="file-picker">
  <label>
    <input type="file"> ファイルを選択
  </label>
</div>
.file-picker label {
  padding: 10px 30px;
  color: white;
  background-color: #0d6efd;
  border-radius: 8px;
  cursor: pointer;
}

.file-picker input[type="file"] {
  display: none;
}

これで次のようにレンダリングされるはずです。

これで「ファイルを選択」という文言も自由に選べるようになりました。実際にファイルが選択された状態なのかそうでないのかを画面上に表示する場合は、JavaScript でその機能を自作することになりますが、面倒である一方で自由度は大きく増します。

とはいえ、はやりハックっぽい方法ではなく、正攻法があるならそれを使いたいですよね。それが showOpenFilePicker() なのです。

showOpenFilePicker() を使う方法

showOpenFilePicker() は、ファイル選択ダイアログを表示して、ユーザーにファイルを選択させるメソッドですが、<input type="file"> と違い、そのトリガーとなる HTML 要素は、クリックを検知できるなら何でも構いません。

次のサンプルは、button 要素のボタンを押したら showOpenFilePicker() メソッドを使ってファイル選択ダイアログを表示し、ユーザーがファイルを選択したら、そのファイルを読み込んで内容をコンソールに出力します。

<button id="btn">ファイルを選択</button>
document.getElementById('btn').addEventListener('click', async () => {
  // ファイル選択ダイアログを表示してユーザーのファイル選択を待つ
  const fh_list = await window.showOpenFilePicker();

  // 選択されたファイルを表す FileHandle オブジェクト
  const fh = fh_list[0];

  // File オブジェクトを取得
  const file = await fh.getFile();

  // ファイルを読み込んで内容をコンソールに出力
  const reader = new FileReader();
  reader.readAsText(file, 'UTF-8');
  reader.onload = (event) => {
    console.log(event.target.result);
  };
});

showOpenFilePicker() メソッドは非同期の処理で Promise オブジェクトを返します。このサンプルでは async の関数の中ですので await を使っています。もしユーザーがファイルを選択せずにキャンセルしたら、showOpenFilePicker() メソッドは例外を投げます。

showOpenFilePicker() メソッドは、ユーザーがファイルを選択すると FileHandle オブジェクトのリストを返します。なぜリストなのかというと、ユーザーが同時に複数のファイルを選択できるからです。ここではユーザーは 1 個のファイルを選択することを前提にしています。その FileHandle オブジェクトは変数 fh に代入しています。

次に、FileHandle オブジェクトの getFile() メソッドを使って File オブジェクトを取り出します。getFile() メソッドも非同期の処理で Promise オブジェクトを返しますので、ここでも await を使っています。

File オブジェクトが取り出せれば、あとは前述の <input type="file"> のサンプルと同じです。

ファイル選択ダイアログの表示オプション

showOpenFilePicker() メソッドには、いくつかのファイル選択ダイアログの表示オプションを引数に指定することができます。

const opts = {
  types: [
    {
      description: 'テキスト',
      accept: {
        'text/plain': ['.txt']
      }
    }
  ],
  multiple: false
};

const fh_list = await window.showOpenFilePicker(opts);

このサンプルでは、text/plain のファイルに限定します。そのファイルの種類の名称として “テキスト” を与えています。さらに、1 つのファイルしか選択できないよう制限しています。

このオプションで指定できるプロパティは次の通りです。

プロパティ説明
typesArray許可するファイルの種類を記述したオブジェクトのリスト。オブジェクトには以下の descriptionaccept プロパティをセットする。
+ descriptionStringファイル種の名称。
+ acceptObjectMIME タイプをキーとして、許可する拡張子 (Array または String) をセットしたオブジェクト。
multipleBoolean複数のファイル選択を許可するなら true をセットします。デフォルトは false です。
excludeAcceptAllOptionBoolean「すべてのファイル (*.*)」の選択を無効にして types に指定したファイル種しか選択できないようにしたいなら true をセットします。デフォルトは false です。

types の指定の方法について、もう少し詳しく説明します。もし次のようにオプションをセットしたとします。

const opts = {
  types: [
    {
      description: 'JAVAソース',
      accept: {
        'text/plain': ['.java']
      }
    }
  ]
};

一見、.java ファイルしか許可しないかのように見えますが、そういうことではありません。OS が text/plain と判断したファイルはファイルピッカーに列挙されます。それに加えて、ここでは .java ファイルもファイルピッカーに表示されることになります。

excludeAcceptAllOption プロパティは「すべてのファイル (*.*)」の選択を無効にするわけですが、「すべてのファイル (*.*)」とはファイル選択ダイアログの右下のプルダウンの選択肢のことです。

excludeAcceptAllOptiontrue をセットすると、この「すべてのファイル (*.*)」の選択肢が表示されなくなります。

const opts = {
  types: [
    {
      description: 'JAVAソース',
      accept: {
        'text/plain': ['.java']
      }
    }
  ],
  excludeAcceptAllOption: true,
  multiple: false
};

まとめ

近年はファイルピッカーはフォームのファイル添付以外の用途で使われることが多くなったこともあり、showOpenFilePicker() が役に立つシーンも増えることでしょう。2022 年 6 月現在では Chrome, Edge といった Chromium 系ブラウザーは showOpenFilePicker() をサポートしていますが、Safari と Firefox はサポートしていません。せめてスマートフォンでシェアが大きい Safari はサポートしてほしいところです。

今回は以上で終わりです。最後まで読んでくださりありがとうございました。それでは次回の記事までごきげんよう。

Share