😎

【イラスト付き】ファイルAPI【利用方法】

2024/09/04に公開

はじめに

皆さんこんにちは。
今回はファイルAPIをご紹介します。

ファイルAPIはブラウザでファイルの読み取りができます。ドラッグ&ドロップと組み合わせてファイルのアップロード処理を作成するなどアプリの利便性の向上が期待できます。今回はファイルAPIの基本的な使い方について扱います。

こんな人にオススメ

  • ファイルAPIの概要が知りたい
  • ファイルAPIの書き方が知りたい

初めて学習する方にも分かるように、要点を絞って丁寧に解説していきます。

😋 ファイルAPIの全体像をご紹介します♪

ファイルAPIとは

まずポイントをチェック

  • ブラウザでファイルを読み取り可能
  • ファイルはユーザーが選択したものに限定される

ファイルAPIとは、ブラウザでファイルを受け取り読み取ることができる機能です。対象のファイルはユーザーが選択したものに限られるので、意図しないファイルが読み取れることはありません。

ブラウザにファイルを渡すには、ファイル選択画面から選択するかドラッグ&ドロップを使います。

ブラウザに渡されたファイルからはファイル名などのプロパティの他、ファイル内容を読み取り利用することができます。選択したファイルのプレビュー表示や、データの読み取りなど様々な処理に利用することができます。

😋 ファイル内容を使った処理の作成が可能になります♪

操作ステップ

まずポイントをチェック

  • ステップ1:ファイルを選択する
  • ステップ2:Fileオブジェクトを取得する
  • ステップ3:Fileオブジェクトに対して処理をする
    • ファイル名の取得やファイルの読み取りなど

ファイルAPIを使ったファイルの読み取りステップをご紹介します。

ステップ1:ファイルを選択する

ユーザーがファイルを指定するには、input要素での選択かドラッグ&ドロップを利用します。ファイルは複数指定可能です。

ステップ2:Fileオブジェクトを取得する

指定されたファイルをFileオブジェクトとして受け取ります。input要素での選択とドラッグ&ドロップのそれぞれで取得方法が少し違います。

FileオブジェクトはFileListという配列のようなものに、指定されたファイル数に応じて格納されています。ここから繰り返し処理などでFileオブジェクトを取得します。

ステップ3:Fileオブジェクトに対して処理をする

Fileオブジェクト取得後はプロパティやファイル内容の読み取りに利用できます。
利用可能なプロパティは、ファイル名・形式・サイズ・最終更新日時です。

ファイルの読み取りはFileReaderオブジェクトを利用します。ファイルの形式に合わせた様々な読み取りメソッドがあり、この読み取りは非同期で行われます。

😋 FileListからの手順はinputとドラッグ&ドロップとも共通です♪

シンプルなコード例

まずポイントをチェック

  • ステップ1:ファイルを選択する
  • ステップ2:Fileオブジェクトを取得する
  • ステップ3:Fileオブジェクトに対して処理をする

サンプル実行画面

操作ステップに沿ったシンプルなコードをご紹介します。

ステップ1:ファイルを選択する

input要素でファイルを選択可能にするにはtype属性に”file”を指定します。複数選択する場合はmultiple属性をつけます。accept属性で指定可能なファイル形式を限定することもできます。

<input type="file" multiple>

ドラッグ&ドロップの場合はファイルをドロップするだけでOKです。ファイルの取得はイベント処理で行います。

ステップ2:Fileオブジェクトを取得する

Fileオブジェクトの受け取りはイベントで行います。input要素もドラッグ&ドロップもどちらもまずはFileListを取得します。

input要素はchangeイベントで処理を行います。input要素自身のfilesプロパティにFileListが格納されています。

fileInput.addEventListener('change', (event) => {
    const fileList = event.target.files;
};

ドラッグ&ドロップはdropイベントで処理を行います。DataTransferオブジェクトのfilesプロパティにFileListが格納されています。

fileDrop.addEventListener('drop', (event) => {
    event.preventDefault();
    const fileList = event.dataTransfer.files
});

FileListからFileオブジェクトを取得するには、for-of文が利用できます。

for (const file of fileList) {
}

なお、FileListのlengthプロパティには選択されたファイル数が格納されています。

fileList.length

ステップ3:Fileオブジェクトに対して処理をする

Fileオブジェクト取得後はプロパティやファイル内容の読み取りを行います。ここでは簡単な参考までにファイル名を取得し画面に表示する処理を掲載します。

// 選択されたファイルの名前を画面に表示する
const showFileContent = (fileList) => {
    for (const file of fileList) {
        const li = document.createElement('li');
        const fileName = document.createTextNode(file.name);
        li.appendChild(fileName);
        resultList.appendChild(li);
    }
};

😋 イベント発生時にFileListを取得し、個別のFileを取り出します♪

プロパティ参照とファイル読み取り

まずポイントをチェック

  • Fileオブジェクトのプロパティからファイルの情報を取得可能
  • ファイルの読み取りはFileReaderを利用する

サンプル実行画面

Fileオブジェクトからファイルに関する情報を取得することができます。

利用可能なプロパティは次のとおりです。

Fileのプロパティ

  • name:ファイル名
  • lastModified:最終更新時刻のミリ秒
  • size:バイト単位のファイルサイズ
  • type:MIMEタイプ
for (const file of fileList) {
    console.log(file.name);
    console.log(file.lastModified);
    console.log(file.size);
    console.log(file.type);
}

ファイルの読み取りはFileReaderオブジェクトのメソッドを使います。読み取り結果に応じた各種メソッドが用意されています。読み取り後に実行する処理はFileReaderオブジェクトのloadイベントのイベントリスナーとして用意します。

FileReaderのメソッド

  • readAsText:読み取り結果をテキストで取得
  • readAsDataURL:読み取り結果をDataURLで取得
  • readAsArrayBuffer:読み取り結果をArrayBufferで取得
const fileReader = new FileReader();
fileReader.addEventListener('load', (event) => { console.log(event.target.result) });
fileReader.addEventListener('error', (event) => { console.log(error));
fileReader.readAsText(file);

😋 ファイル読み取りは非同期で行われます♪

CSV、JSONの読み取り

まずポイントをチェック

  • readAsTextメソッドで読み取り
  • CSVファイルはカンマで分割して個別の値を取得
  • JSONファイルはJSON.parseでオブジェクトに変換

サンプル実行画面

ファイル読み取りの例として、CSVとJSONファイルの読み取りについてご紹介します。

CSVとJSONはどちらもFileReaderオブジェクトのreadAsTextメソッドで読み取ります。

const readFile = (file) => {
    return new Promise((resolve, reject) => {
        const fileReader = new FileReader();
        fileReader.addEventListener('load', (event) => { resolve(event.target.result) });
        fileReader.addEventListener('error', (event) => { reject('failed to read file') });
        fileReader.readAsText(file);
    });
};

CSVの場合は読み取り結果の文字列をカンマ区切りで分割し、それぞれの値を取得します。

サンプルコードではtypeプロパティで指定されたファイルがCSV形式かチェックをしています。その後String型のsplitメソッドで改行ごとに区切って配列にします。その配列から1行ずつカンマ区切りで値を取り出します。最終的に全ての値を1つの配列にまとめています。

if (file.type === 'text/csv') {
    const csvFile = await readFile(file);
    const lines = csvFile.split('\n'); // 行で区切った配列
    
    const result = [];
    for (const line of lines) {
        const values = line.split(','); // カンマで区切った配列
        result.push(...values); // 値を1つの配列にまとめる
    }
    readResult = result;
}

JSONの場合は読み取り結果の文字列をJavaScriptオブジェクトに変換しています。

JSONからJavaScriptオブジェクトへの変換はJSON.parseメソッドを利用します。その後JavaScriptオブジェクトを利用する例として再度JSON形式に変換しています。

if (file.type === 'application/json') {
    const jsonFile = await readFile(file);
    const jsonObj = JSON.parse(jsonFile);
    readResult = JSON.stringify(jsonObj);
    // オブジェクトを用いることの例として、JSON形式に再変換(無駄コード)
}

😋 readAsTextで読み取り、形式に沿って文字列を加工します♪

画像の読み取り

まずポイントをチェック

  • readAsDataURLメソッドで読み取り
  • img要素のsrc属性に読み取り結果を渡すと表示可能

サンプル実行画面

画像の読み取りはreadAsDataURLメソッドで行います。読み取り結果のデータURLをimg要素のsrc属性にそのまま渡すと画面に表示することができます。

const readFile = (file) => {
    return new Promise((resolve, reject) => {
        const fileReader = new FileReader();
        fileReader.addEventListener('load', (event) => { resolve(event.target.result) });
        fileReader.addEventListener('error', (event) => { reject('failed to read file') });
        fileReader.readAsDataURL(file);
    });
};
const dataUrl = await readFile(file);
const img = new Image();
img.src = dataUrl;

😋 readAsDataURLで読み取り、imgのsrcで表示できます♪

Excelの読み取り

まずポイントをチェック

  • SheetJSライブラリを利用
  • readAsArrayBufferメソッドでFileオブジェクトを読み取り
  • SheetJSのXLSX.readメソッドでExcelの内容を取得

サンプル実行画面

Excelの読み取りを行う場合はSheetJSライブラリを利用すると便利です。大まかな利用方法をご紹介します。

ファイルの読み取りはFileReaderオブジェクトのreadAsArrayBufferメソッドで行います。

const readFile = (file) => {
    return new Promise((resolve, reject) => {
        const fileReader = new FileReader();
        fileReader.addEventListener('load', (event) => { resolve(event.target.result) });
        fileReader.addEventListener('error', (event) => { reject('failed to read file') });
        fileReader.readAsArrayBuffer(file);
    });
};

読み取り結果はXLSX.readメソッドに渡します。生成したworkBookオブジェクトからデータを取得します。Sheetsでシート名を指定し個別のシートを読み取ります。

for (const file of fileList) {
    const excelData = await readFile(file);
    const workbook = XLSX.read(excelData);
    const sheet = workbook.Sheets['Sheet1'];
}

XLSX.utils.sheet_to_csvメソッドでCSV形式で取得します。A1セルからExcelの内容通りのCSV形式の文字列を取得します。

// シートの内容をCSV形式の文字列で取得
const csvData = XLSX.utils.sheet_to_csv(sheet);

XLSX.utils.sheet_to_jsonメソッドでJSON形式で取得します。A1セルから先頭行を項目名として取得し、2行目以降を1つのオブジェクトとして取得します。

// シートの内容をJSON形式の配列で取得
const jsonData = XLSX.utils.sheet_to_json(sheet);

個別のセルから値を取得するにはセルアドレスを指定します。表示データを取得する場合はwプロパティを指定します。なお、vプロパティには生データが入っています。

// セルのアドレスを指定して取得
const cel = sheet['A1'];
    
// vは生データ、wは表示データ
console.log(cel.w);

😋 readAsArrayBufferの結果をSheetJSライブラリに渡します♪

オブジェクトURLの活用

まずポイントをチェック

  • URL.createObjectURLメソッドを使ってオブジェクトURLを生成可能
  • img、audio、video、iframe、a要素などで表示やダウンロードリンクに利用


サンプル実行画面

input要素やドラッグ&ドロップで受け取ったFileオブジェクトを、オブジェクトURLに変換することができます。URL.createObjectURLメソッドの引数にFileオブジェクトを指定します。

const objectUrl = URL.createObjectURL(file);

生成したオブジェクトURLをimg、audio、video、iframe要素などのsrc属性に指定することで画面に表示することができます。またa要素のhref属性に指定することでダウンロードリンクを生成することができます。

<img src=${objectUrl}>
<audio src=${objectUrl} controls></audio>
<video src=${objectUrl} controls></video>
<iframe src=${objectUrl}></iframe>
<a href=${objectUrl} download>ここをクリック!</a>

オブジェクトURLはブラウザ内のメモリに保持されるため、ダウンロードやプレビューなどの利用後は解放する必要があります。URL.revokeObjectURLメソッドの引数にオブジェクトURLを指定します。なおオブジェクトURLを利用してるページから離れると自動的に解放されます。

URL.revokeObjectURL(objectUrl);

😋 コンテンツの表示やダウンロードに利用できます♪

【おまけ】フォルダごとドロップして読み取り

まずポイントをチェック

  • ステップ1:ファイルシステムのエントリーを取得
  • ステップ2:エントリーをファイルとフォルダで分別
  • ステップ3:Fileオブジェクトを取得
  • ステップ4:Fileオブジェクトの内容を画面表示

サンプル実行画面

ファイルAPIではフォルダを扱うことができません。フォルダのドロップ操作を行うにはFile System Access APIを利用します。File System Access APIはファイルシステムにアクセスしファイルやフォルダを扱うAPIです。

以下のステップはサンプルコードにおけるフォルダ内を画面表示する処理工程です。

大雑把にやりたいことはDataTransferItemの取得、エントリーを取得、Fileを取得の3つです。Fileオブジェクトを取得さえすれば、あとは通常通り読み取って表示するだけです。

ステップ1:ファイルシステムのエントリーを取得

dropイベントでevent.dataTransfer.itemsを参照し、ドロップされたDataTransferItemのリストを取得します。DataTransferItemはドロップされたデータを表すオブジェクトです。

fileDrop.addEventListener('drop', (event) => {
    event.preventDefault();
    const items = event.dataTransfer.items;
}

DataTransferItemのwebkitGetAsEntryメソッドでFileSystemDirectoryEntryやFileSystemFileEntryを取得します。これらのエントリーはファイルシステムのフォルダやファイルを表します。

fileDrop.addEventListener('drop', (event) => {
    event.preventDefault();
    const items = event.dataTransfer.items;

    const entries = [];
    for (const item of items) {
        // ドロップされたモノをエントリーとして取得
        entries.push(item.webkitGetAsEntry());
    }
});

ステップ2:エントリーをファイルとフォルダで分別

エントリーのisDirectoryプロパティとisFileプロパティでファイルとフォルダを判別します。フォルダの場合はさらに中のファイルを取得したいので、フォルダ内のエントリーを読み取り、同じように分別を再起的に行います。フォルダがなくなるまで繰り返したら全ての階層を読み取ったことになります。

// エントリーをフォルダとファイルで分別
const devideEntries = async (dirEntries, fileEntries, entries) => {
    for (const entry of entries) {
        // エントリーがフォルダの場合、この処理を再帰的に呼び出し
        if (entry.isDirectory) {
            dirEntries.push(entry);
            // そのフォルダ内のエントリーを取得
            const innerEntries = await getEntriesFromDir(entry);
            // エントリーをフォルダとファイルで分別(再帰呼び出し)
            await devideEntries(dirEntries, fileEntries, innerEntries);
        }
        if (entry.isFile) {
            fileEntries.push(entry);
        }
    }
};

FileSystemDirectoryEntryからさらに内側のファイルやフォルダのエントリーを取得するにはcreateReaderメソッドで生成したオブジェクトのreadEntriesメソッドを利用します。読み取りは非同期で行われるため、Promise化すると分かりやすくなります。

// エントリーの取得(非同期処理をPromise化)
const getEntriesFromDir = (dirEntry) => {
    return new Promise((resolve, reject) => {
        const dirReader = dirEntry.createReader();
        dirReader.readEntries(
            entries => resolve(entries),
            error => reject('failed to get entries'));
    });
};

ステップ3:Fileオブジェクトを取得

FileSystemFileEntryからFileオブジェクトを取得するにはfileメソッドを利用します。このメソッドは非同期で行われるため、Promise化すると分かりやすくなります。

// Fileオブジェクトの取得(非同期処理をPromise化)
const getFileFromEntry = (fileEntry) => {
    return new Promise((resolve, reject) => {
        // ファイルエントリーからファイルオブジェクトを取得
        fileEntry.file(
            file => resolve(file),
            error => reject('failed to get file'));
    });
};

ステップ4:Fileオブジェクトの内容を画面表示

あとはFileオブジェクトの内容を読み取り画面に表示します。サンプルコードではファイルとフォルダの紐付きを確認できるようにするため、パスとファイル内容をオブジェクトにまとめて、フォルダ名の下にそのフォルダに属するファイルを表示するようにしています。

😋 フォルダはFile System Access APIで読み取ります♪

おわりに

皆さん、お疲れ様でした。
ここまでご覧いただき、ありがとうございました。

ファイルAPIについて確認をしていただきました。
ブラウザでファイルを受け取り、プレビューやサーバーに送信するなどの対応ができるようになります。
ファイルのドロップ操作はブラウザに既定の処理が組み込まれているので、キャンセルも忘れずに行いましょう。

😋 これからもプログラミング学習頑張りましょう♪

参考リンク集(MDN Web Docs のリンク)
参考リンク集(web.dev のリンク)
参考リンク集(その他 のリンク)
参考リンク集(サンプルコード)

Discussion