ユーザーのローカル デバイスでファイルを選択して操作することは、ウェブで最もよく使用されている機能の 1 つです。ユーザーは、写真の共有や税務書類の送信などの際に、ファイルを選択してサーバーにアップロードできます。また、サイトはネットワーク経由でデータを転送しなくても、データを読み取って操作できます。このページでは、JavaScript を使用してファイルを操作する方法について説明します。
最新の File System Access API
File System Access API は、ユーザーのローカル システム上にあるファイルとディレクトリの読み書きを行います。この機能は Chrome や Edge など、Chromium ベースのほとんどのブラウザで利用できます。詳細については、File System Access API をご覧ください。
File System Access API はすべてのブラウザに対応しているわけではないため、browser-fs-access の使用をおすすめします。このヘルパー ライブラリは、新しい API が利用可能な場合は常に新しい API を使用し、使用できない場合は以前のアプローチにフォールバックします。
従来の方法でファイルを操作する
このガイドでは、従来の JavaScript メソッドを使用してファイルを操作する方法について説明します。
ファイルの選択
ファイルを選択する主な方法は 2 つあります。HTML 入力要素を使用する方法と、ドラッグ&ドロップ ゾーンを使用する方法です。
HTML 入力要素
ユーザーがファイルを選択する最も簡単な方法は、すべての主要なブラウザでサポートされている <input type="file">
要素を使用することです。ユーザーがクリックすると、オペレーティング システムの組み込みのファイル選択 UI を使用して、1 つまたは複数のファイルを選択できます(multiple
属性が含まれている場合)。ユーザーが 1 つまたは複数のファイルの選択を完了すると、要素の change
イベントが発生します。ファイルのリストには、FileList
オブジェクトである event.target.files
からアクセスできます。FileList
の各アイテムは File
オブジェクトです。
<!-- The `multiple` attribute lets users select multiple files. -->
<input type="file" id="file-selector" multiple>
<script>
const fileSelector = document.getElementById('file-selector');
fileSelector.addEventListener('change', (event) => {
const fileList = event.target.files;
console.log(fileList);
});
</script>
次の例では、ユーザーはオペレーティング システムに組み込まれているファイル選択 UI を使用して複数のファイルを選択し、選択した各ファイルをコンソールに記録できます。
ユーザーが選択できるファイルの種類を制限する
場合によっては、ユーザーが選択できるファイルの種類を制限することもできます。たとえば、画像編集アプリは画像のみを受け付け、テキスト ファイルは使用できません。ファイル形式の制限を設定するには、入力要素に accept
属性を追加して、使用可能なファイル形式を指定します。
<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">
カスタムのドラッグ&ドロップ
一部のブラウザでは、<input type="file">
要素がドロップ ターゲットでもあり、ユーザーはファイルをアプリ内にドラッグ&ドロップできます。ただし、このドロップ ターゲットはサイズが小さく、使いにくい可能性があります。代わりに、<input type="file">
要素を使用してコア機能を提供した後、大きなカスタム ドラッグ&ドロップ サーフェスを指定できます。
ドロップゾーンを選択する
ドロップ サーフェスはアプリケーションの設計によって異なります。ウィンドウの一部のみをドロップ サーフェスにすることもできますが、ウィンドウ全体を使用できます。
画像圧縮アプリ Squoosh を使用すると、ユーザーはウィンドウ内の任意の場所に画像をドラッグし、[画像を選択] をクリックして <input type="file">
要素を呼び出すことができます。ドロップゾーンとしてどのゾーンを選択するにしても、そのサーフェスにファイルをドラッグできることがユーザーにとって明確であることを確認してください。
ドロップゾーンを定義する
要素をドラッグ&ドロップ ゾーンとして有効にするには、2 つのイベント(dragover
と drop
)のリスナーを作成します。dragover
イベントによってブラウザ UI が更新され、ドラッグ&ドロップ アクションによってファイルのコピーが作成されていることが視覚的に示されます。drop
イベントは、ユーザーがファイルをサーフェスにドロップすると呼び出されます。入力要素と同様に、FileList
オブジェクトである event.dataTransfer.files
からファイルのリストにアクセスできます。FileList
内の各アイテムは File
オブジェクトです。
const dropArea = document.getElementById('drop-area');
dropArea.addEventListener('dragover', (event) => {
event.stopPropagation();
event.preventDefault();
// Style the drag-and-drop as a "copy file" operation.
event.dataTransfer.dropEffect = 'copy';
});
dropArea.addEventListener('drop', (event) => {
event.stopPropagation();
event.preventDefault();
const fileList = event.dataTransfer.files;
console.log(fileList);
});
event.stopPropagation()
と event.preventDefault()
はブラウザのデフォルトの動作を停止し、代わりにコードを実行します。そうしないと、ブラウザはページから離れ、ユーザーがブラウザ ウィンドウにドロップしたファイルを開きます。
ライブデモについては、カスタム ドラッグ&ドロップをご覧ください。
ディレクトリについてはどうでしょうか。
残念ながら、JavaScript を使用してディレクトリにアクセスする適切な方法はありません。
<input type="file">
要素の webkitdirectory
属性を使用すると、ユーザーは 1 つまたは複数のディレクトリを選択できます。Android 版 Firefox と iOS 版 Safari を除き、ほとんどの主要なブラウザでサポートされています。
ドラッグ&ドロップが有効になっている場合、ユーザーがドロップゾーンにディレクトリをドラッグしようとすることがあります。ドロップ イベントが発生すると、ディレクトリの File
オブジェクトが含まれますが、ディレクトリ内のどのファイルにもアクセスできなくなります。
ファイルのメタデータを読み取る
File
オブジェクトには、ファイルに関するメタデータが含まれます。ほとんどのブラウザでは、ファイル名、ファイルのサイズ、MIME タイプが表示されますが、プラットフォームによっては、提供される情報や追加情報が異なる場合があります。
function getMetadataForFileList(fileList) {
for (const file of fileList) {
// Not supported in Safari for iOS.
const name = file.name ? file.name : 'NOT SUPPORTED';
// Not supported in Firefox for Android or Opera for Android.
const type = file.type ? file.type : 'NOT SUPPORTED';
// Unknown cross-browser support.
const size = file.size ? file.size : 'NOT SUPPORTED';
console.log({file, name, type, size});
}
}
これは、input-type-file
デモで実際に確認できます。
ファイルの内容を読み取る
FileReader
を使用して、File
オブジェクトのコンテンツをメモリに読み取ります。ファイルを配列バッファ、データ URL、テキストとして読み取るように FileReader
に指示できます。
function readImage(file) {
// Check if the file is an image.
if (file.type && !file.type.startsWith('image/')) {
console.log('File is not an image.', file.type, file);
return;
}
const reader = new FileReader();
reader.addEventListener('load', (event) => {
img.src = event.target.result;
});
reader.readAsDataURL(file);
}
この例では、ユーザーが指定した File
を読み取り、それをデータ URL に変換し、そのデータ URL を使用して img
要素内に画像を表示します。ユーザーが画像ファイルを選択していることを確認する方法については、read-image-file
デモをご覧ください。
ファイルの読み取りの進行状況をモニタリングする
大きなファイルを読み取るときは、読み取りの進捗状況をユーザーに伝える UX を用意すると便利です。そのためには、FileReader
が提供する progress
イベントを使用します。progress
イベントには、loaded
(読み取り量)と total
(読み取り量)の 2 つのプロパティがあります。
function readFile(file) {
const reader = new FileReader();
reader.addEventListener('load', (event) => {
const result = event.target.result;
// Do something with result
});
reader.addEventListener('progress', (event) => {
if (event.loaded && event.total) {
const percent = (event.loaded / event.total) * 100;
console.log(`Progress: ${Math.round(percent)}`);
}
});
reader.readAsDataURL(file);
}
ヒーロー画像(作成者: Vincent Botta、Unsplash)