JavaScript Tips - アップロードしたローカルファイルの中身を読み込んで表示する方法
はじめに
今回は, 昨日のエントリー
とは逆に, アップロードしたローカルのファイルを読み込んで表示する方法をサンプルを交えて紹介します.
読み込み自体は簡単なんですが, 標準の FileReader だとちょっとレガシーな書き方になったしまうのでラップして Promise オブジェクトを返すようにしてみました.
こうすることで使う側は async/await でスッキリ書くことができます.
よかったら参考にしてください.
サンプルプログラム
ローカルにあるテキストファイルをアップロードすると, 中身を読み込んで内容をテキストエリアに反映します.
コード
<body>
<h1>${title}</h1>
<p>${description}</p>
<input id='$inputFile' type='file' />
<div>
<textarea id='$input' rows='8' cols='64'>hoge foo bar</textarea>
</div>
<a id='$btn' class='btn' download='sample.txt'>入力した内容を保存</a>
<script>${script}</script>
</body>
window.onload = function() {
// ダウンロードボタンを押した際のイベントを登録
$btn.onclick = () => {
// blob オブジェクトを生成
var content = $input.value;
var blob = new Blob([content], { type: 'text/plain' });
// download の href に object url を設定
$btn.href = window.URL.createObjectURL(blob);
};
$inputFile.onchange = async (e) => {
// ファイルオブジェクトを取得
var file = e.currentTarget.files[0];
if (!file) return ;
// 中身を取得
var text = await fetchAsText(file);
// テキストエリアに代入
$input.value = text;
};
};
// ファイルから内容をテキストとして取得する Promise を返す
var fetchAsText = (file) => {
return new Promise((resolve) => {
var fr = new FileReader();
fr.onload = (e) => {
// 読み込んだ結果を resolve する
resolve(e.currentTarget.result);
};
// 読み込む
fr.readAsText(file);
});
};
解説
ファイルアップロード用の input 要素を用意しよう
type
属性に file
を指定した input
要素を用意しましょう.
<input id='$inputFile' type='file' />
これでクリックするとファイル選択モーダルが出るようになります!
ファイルの中身を読み込んで返す関数を定義しよう
ファイル読み込みを普通に書いてしまうとネストが深くなってしまうので, 使う側のコードがスッキリするよう関数化して Promise オブジェクトを返すようにしましょう.
やってることはシンプルで, File オブジェクトを受け取ったらその内容を FileReader 経由で読み込んで, 読み込んだ内容を resolve するようにしています.
// ファイルから内容をテキストとして取得する Promise を返す
var fetchAsText = (file) => {
return new Promise((resolve) => {
var fr = new FileReader();
fr.onload = (e) => {
// 読み込んだ結果を resolve する
resolve(e.currentTarget.result);
};
// 読み込む
fr.readAsText(file);
});
};
これで使う側は
var text = await fetchAsText(file);
という感じでスッキリ使うことができるようになります.
ファイルが選択されたら内容を読み込んでテキストエリアに反映しよう
最後に, ファイルを選んだときに先程定義した fetchAsText()
関数を実行するようにしましょう.
ファイルを選択すると input
の change
イベントが発火するので, その中で選択した File オブジェクトを fetchAsText()
に渡して実行し, 結果の text
をテキストエリアの value
に代入しています.
$inputFile.onchange = async (e) => {
// ファイルオブジェクトを取得
var file = e.currentTarget.files[0];
if (!file) return ;
// 中身を取得
var text = await fetchAsText(file);
// テキストエリアに代入
$input.value = text;
};
これで, ファイルを選択した際, 中身をテキストエリアに反映するようになります.
おわりに
昔の仕様で作られた機能は Promise 対応していないので結構回りくどい書き方をしないといけなかったりするんですよね💦
Web Audio の decodeAudioData とかは, 最近 Promise 対応してくれてだいぶスッキリ変えるようになったりしてるので, FileReader もいずれは Promise 対応してくれることを期待しています.
それまでは, 一通りラップしたライブラリを作って凌ぐのもありかもですね!
今回のエントリーも, 昨日のエントリー 同様, 管理ツールなんかで csv ファイルを読み込んで反映する際なんかによく使う tips です.
出力と読み込みができればわりとライトなツールをサーバーレスで作れたりもするのでぜひ活用して遊んでみてください.
Discussion