Article Outline
JS 画像のアップロード、プレビュー機能を実装
TOC
Collection Outline
最新記事
- Next.js 北九州市のイベント情報を取得する
- Next.js × now がやばい
- 競プロ日記 ABC15
- 競プロ日記 ABC159
- Kaggle 住宅価格予測 単回帰分析してみる
- 単回帰分析のFlow
- React Native Styleをいい感じに
- React Movie Search を TS で.
- useRefの使い方がわからん
- Heroku PostgreSQL の使い方
- ラズパイとLEDマトリクスパネルで遊ぶ
- querySelectorAllで取得した要素は配列ではないらしい
- React HookでTodoアプリを作る
- Kaggle 住宅価格予測 重回帰分析してみる
- Kaggle 住宅価格予測 多項式回帰してみる
- Webフロントのすべて
- 新しいMacBook Airが届いた!
- 日向坂46のニュースをスクレイピング
- イコラブのニュースをスクレイピングする!
- 推しのブログをスクレイピング
HTML部分
<label for="image">アップロードする画像を選択してください。</label>
<input type="file" id="image" accept="image/*" name="image" multiple>
<div class="container"></div>
inputのtypeをfileにするとファイルをアップロードできるようになります。
そしてacceptでアップロードできるファイルを絞っています。今回は画像のみにしたいのでimage/*
にしました。 jpg, png,
gifとかをアップロードできます。
multiple属性は複数アップロードできるようにするためです。
containerの中に画像のプレビューをしていきます。
単純な実装
1枚の画像のみの場合は、こんな感じでできます。
element.addEventListener("input", (event) => {
const target = event.target;
const files = target.files;
const file = files[0];
const reader = new FileReader();
reader.onload = () => {
const img = new Image();
img.src = reader.result;
container.appendChild(img);
};
reader.readAsDataURL(file);
});
filesのなかにアップロードした画像があります。
FileReaderは画像をプレビューするためにDataURLとして読み取ってくれます。(非同期)
FileReader.onloadで読み取りが終わったら画像を表示させてます。
この実装でも良いんですけどちょっと単調なので、
- FileReaderで読み取るところを関数化したい
- 複数枚に対応させたい
です。
なのでもう少しいじっていきます.
Readerを関数化
こちらの通りです
参考
https://blog.shovonhasan.com/using-promises-with-filereader/
const readFileAsDataURL = (file) => {
const reader = new FileReader();
return new Promise((resolve, reject) => {
reader.onerror = () => {
reader.abort();
reject(new DOMException("Problem parsing input file."));
};
reader.onload = () => {
resolve(reader.result);
};
reader.readAsDataURL(file);
});
};
Promiseにすることで関数化とawaitが使えるようになってとても嬉しいですね。
エラーが起きたときは読み取りを中断してreject, 読み取りが完了したら結果をresolveしています。
複数枚アップロードとエラーの対処など
fileListは配列のように見せかけてオブジェクトだったんですよねぇ。。
なのでArray.fromメソッドでくるくるしながらさっきの関数に通してプロミスの配列を作っていきます。
そしてPromise.allで一気に実行!
もしrejectされてるやつがあったらエラー起きちゃうのでcatchでの補足とfilterでエラーが起きたやつを取り除いています。
最後にプレビューしていきます。
element.addEventListener("input", async (event) => {
const target = event.target;
const files = target.files;
const arrPromise = Array.from(
files,
(file) => readFileAsDataURL(file).catch((e) => e),
);
const results = await Promise.all(arrPromise);
// Errorを弾く
const validResults = results.filter((result) => !(result instanceof Error));
for (const result of validResults) {
const img = new Image();
img.src = result;
container.appendChild(img);
}
});
コード全体
<main>
<style>
.container {
display: flex;
flex-wrap: wrap;
}
</style>
<label for="image">アップロードする画像を選択してください。</label>
<input type="file" id="image" accept="image/*" name="image" multiple>
<div class="container"></div>
</main>
<script>
const element = document.querySelector('#image')
const container = document.querySelector('.container')
const readFileAsDataURL = (file) => {
const reader = new FileReader()
return new Promise((resolve, reject) => {
reader.onerror = () => {
reader.abort()
reject(new DOMException('Problem parsing input file.'))
}
reader.onload = () => {
resolve(reader.result)
}
reader.readAsDataURL(file)
})
}
element.addEventListener('input', async (event) => {
const target = event.target
const files = target.files
const arrPromise = Array.from(files, file => readFileAsDataURL(file).catch(e => e))
const results = await Promise.all(arrPromise)
// Errorを弾く
const validResults = results.filter(result => !(result instanceof Error))
for (const result of validResults) {
const img = new Image()
img.src = result
container.appendChild(img)
}
})
</script>
実行結果
感想
業務コードだったらこのぐらい保守しなきゃですね