はじめに
fileReaderを使って複数の画像をまとめてアップロードする。
OpenProcessingの環境が便利なのでそこで実験する。
コード全文
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.9/lib/p5.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.9/lib/addons/p5.sound.min.js"></script>
<script src="mySketch.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<input type="file" id="uploader" multiple>
<div id="preview-images"></div>
</body>
</html>
function initialize(){
const fileStuck = [];
document.getElementById('uploader').addEventListener('change',(e)=>{
const files = e.target.files;
fileStuck.length = 0;
for(const file of files){
fileStuck.unshift(file);
}
// fileStuckが空になるまで表示処理
const pi = document.getElementById("preview-images");
const reader = new FileReader();
const createPreview = (file) => {
reader.readAsDataURL(file);
reader.onload = () => {
const src = reader.result;
const img = document.createElement("img");
img.src = src;
img.width = 144;
img.classList.add("loaded-image");
const p = document.createElement("p");
p.classList.add("loaded-image-name");
p.innerText = file.name;
const div = document.createElement("div");
div.classList.add("preview-unit");
div.appendChild(p);
div.appendChild(img);
pi.appendChild(div);
if(fileStuck.length > 0){
const nextFile = fileStuck.pop();
createPreview(nextFile);
}
}
}
if(fileStuck.length > 0){
createPreview(fileStuck.pop());
}
});
}
document.addEventListener("DOMContentLoaded", initialize);
function setup() {
console.clear();
createCanvas(400,400);
noCanvas();
}
html,
body {
margin: 0;
padding: 0;
}
#preview-images{
display:flex;
width:100%;
flex-wrap: wrap;
}
.loaded-image{
margin:20px;
pointer-events: none;
margin:10px;
}
.loaded-image-name{
pointer-events: none;
margin:10px;
}
.preview-unit{
background:linear-gradient(to right bottom, white, black);
}
実行結果
複数の画像を選択する:
すべてロードされる。ファイルはオブジェクトなどで別途管理する必要がある。
今回はロードして並べるだけ。
FileReader
ロードするにはFileReaderを用意してこれのreadAsDataURLという関数を使う。そのあとでロードが完了したタイミングでresultにソースが入るので、これをセットする。ただ処理中に次のファイルに対してreadAsDataURLを実行するとエラーになるので、単純にfor-loopでこの処理を書くと失敗する。
この記事では、それを回避するためにスタックを使っている。まず取得した複数のファイルを新しいものが先頭に来るようにスタックに入れる。
fileStuck.length = 0;
for(const file of files){
fileStuck.unshift(file);
}
そして中身を出して実行する。onload内部で処理が終わった時にスタックが残っていれば、さらに出す。
if(fileStuck.length > 0){
const nextFile = fileStuck.pop();
createPreview(nextFile);
}
こうすれば処理中に次のファイルに対する処理が始まるのを防ぐことができる。以上です。
おわりに
console.clear()便利ですね。OpenProcessingの環境は変なエラーがたくさん出るのでこれで全部最初に消してしまうといいです。
ここまでお読みいただいてありがとうございました。
追記
あのあとPromise.withResolversを使って書き直すことを提案されて、理解が不十分なんですが一応書き直してみました。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.7/lib/p5.min.js"></script>
<script src="mySketch.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<input type="file" id="uploader" multiple>
<div id="preview-images"></div>
</body>
</html>
// Promise.withResolversで書き直し:https://qiita.com/inaba_darkfox/items/a601a7aaa3e28fb2bc93
async function createPreview(reader, file){
reader.readAsDataURL(file);
const {resolve, reject, promise} = Promise.withResolvers();
reader.onload = resolve;
reader.onerror = reject;
await promise; // これでonloadが済んでそれ以降の処理を実行するらしい
const src = reader.result;
const pi = document.getElementById("preview-images");
const img = document.createElement("img");
img.src = src;
img.classList.add("loaded-image");
const p = document.createElement("p");
p.classList.add("loaded-image-name");
p.innerText = file.name;
const div = document.createElement("div");
div.classList.add("preview-unit");
div.appendChild(p);
div.appendChild(img);
pi.appendChild(div);
}
async function createPreviewAll(files){
const reader = new FileReader();
for(const file of files){
await createPreview(reader, file);
}
}
function initialize(){
document.getElementById('uploader').addEventListener('change',(e)=>{
const files = e.target.files;
createPreviewAll(files)
});
}
document.addEventListener("DOMContentLoaded", initialize);
function setup() {
console.clear();
createCanvas(400,400);
noCanvas();
}
html,
body {
margin: 0;
padding: 0;
}
#preview-images{
display:flex;
width:100%;
flex-wrap: wrap;
}
.loaded-image{
pointer-events: none;
width:180px;
margin:10px;
}
.loaded-image-name{
pointer-events: none;
margin:10px;
overflow-wrap: break-word;
}
.preview-unit{
width:200px;
background:linear-gradient(to right bottom, white, black);
}
若干変更しました。unitを200px幅で統一して、画像のサイズをその90%にしました。cssをいじってファイル名がはみ出さないようにしてあります。配列で処理できるのでstuckは破棄してfilesを直接使うことにしました。もともとこのスタイルで書きたかったのでありがたいです...
ドラッグドロップとかでp5と組み合わせて使うのも楽しそうです。以上です。
もともとこのコードは自サイトでロードした画像をFFmpegで圧縮してキャンバスに落としてダウンロードするのをやりたくて用意したのでした(随時修正中)。
圧縮前(4608x3456, 3.80MB)
圧縮後(1920x1440, 400KB)