File APIを使用して画面内でファイルデータを保持する
目次
概要
今回はWeb APIの1つであるFile APIを使用して画面内にファイルデータを保持したいと思います。
input
タグはファイルを選択した状態で再度ファイルを選択すると、その前のファイルは選択が保持されずクリアされてしまいます。
しかし、ファイルデータを保持したままファイルの選択を行いたいことがあると思います。
File APIについて
File APIはMDNのドキュメントにある通り、WebアプリケーションがファイルにアクセスするためのAPIとなります。
今回フォーカスするのははFile
オブジェクトとFileList
オブジェクトになります。
input
タグから選択したファイルはFileList
オブジェクトにFile
オブジェクトとして複数格納されています。
これらを使用し、input
タグから読み込んだファイルを保持し、ファイル名を一覧で表示する画面を作成します。
また、ボタンを押すとファイルのダウンロードのリンクを表示する処理も作成します。
実装
HTMLにてファイルを読み込むinput
タグとダウンロードするためのリンクを表示するボタンを配置します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
</head>
<body>
<div>
<input type="file" id="file" name="file" multiple />
<div id="fileList"></div>
</div>
<div>
<button type="button" id="download">
ダウンロードファイルを表示する
</button>
<div id="downloadFileList"></div>
</div>
<script type="module" src="/src/fileAPI.ts"></script>
</body>
</html>
HTML内で操作する要素を指定します。
const inputFileElement = () =>
document.querySelector<HTMLInputElement>("#file");
const buttonDownloadElement = () =>
document.querySelector<HTMLButtonElement>("#download");
const divFileListElement = () =>
document.querySelector<HTMLDivElement>("#fileList");
const divDownloadFileListElement = () =>
document.querySelector<HTMLDivElement>("#downloadFileList");
保持するファイルを格納する状態の宣言を行います。
let files: File[] = [];
次のコードはinput
タグから読み込んだファイルを保持し、ファイル名を一覧表示するコードになります。
const selectFileEvent = (event: Event): void => {
const target = event.target as HTMLInputElement;
const fileList = target.files;
if (fileList === null) return;
for (let i = 0; i < fileList.length; i++) {
const file = fileList.item(i);
if (file === null) continue;
let li = document.createElement("li");
li.textContent = file.name;
divFileListElement()?.appendChild(li);
files = [...files, file];
}
};
inputFileElement()?.addEventListener("change", selectFileEvent);
FileList
オブジェクトは配列ではないため、FileList
オブジェクトのプロパティであるlength
からinput
タグで選択したファイルの数を取得し、for
文を使用します。
個々のファイルデータはFileList
オブジェクトのitem()
メソッドでインデックスを指定することで取得することが出来ます。
次のコードは、保持したファイルのダウンロードリンクを表示するコードになります。
const showDownloadURLEvent = (): void => {
divDownloadFileListElement()
?.querySelectorAll("li")
?.forEach((elem) => elem.remove());
files.map((file) => {
let anchor = document.createElement("a");
anchor.href = URL.createObjectURL(file);
anchor.download = file.name;
anchor.textContent = file.name;
let li = document.createElement("li");
li.appendChild(anchor);
divDownloadFileListElement()?.appendChild(li);
});
};
buttonDownloadElement()?.addEventListener("click", showDownloadURLEvent);
ダウンロードのリンクはURL.createObjectURL()
メソッドを使用して作成しています。
また、ボタンを押すたびにリンクとなる要素を全て削除して再度表示しています。
まとめ
今回はFile APIを使用して画面内でファイルのデータを一時的に保持する処理を実装しました。
File APIを使用することでinput
タグのみの場合よりもファイル選択の自由度が増したと思います。
今回は紹介しませんでしたが、ここで保持したデータはリクエストデータとしても使用出来ます。
今回作成したtypescriptのコードの全体は以下になります。
// HTML内で操作する要素の指定
const inputFileElement = () =>
document.querySelector<HTMLInputElement>("#file");
const buttonDownloadElement = () =>
document.querySelector<HTMLButtonElement>("#download");
const divFileListElement = () =>
document.querySelector<HTMLDivElement>("#fileList");
const divDownloadFileListElement = () =>
document.querySelector<HTMLDivElement>("#downloadFileList");
// 状態の宣言
let files: File[] = [];
// ファイルを保持してファイル名を表示する関数
const selectFileEvent = (event: Event): void => {
const target = event.target as HTMLInputElement;
const fileList = target.files; // FileList
if (fileList === null) return;
for (let i = 0; i < fileList.length; i++) {
const file = fileList.item(i);
if (file === null) continue;
let li = document.createElement("li");
li.textContent = file.name;
divFileListElement()?.appendChild(li);
files = [...files, file];
}
};
// 保持したファイルのダウンロードリンクを表示する関数
const showDownloadURLEvent = (): void => {
divDownloadFileListElement()
?.querySelectorAll("li")
?.forEach((elem) => elem.remove());
files.map((file) => {
let anchor = document.createElement("a");
anchor.href = URL.createObjectURL(file);
anchor.download = file.name;
anchor.textContent = file.name;
let li = document.createElement("li");
li.appendChild(anchor);
divDownloadFileListElement()?.appendChild(li);
});
};
// イベントリスナーの設定
inputFileElement()?.addEventListener("change", selectFileEvent);
buttonDownloadElement()?.addEventListener("click", showDownloadURLEvent);
Discussion