2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

複数の画像をまとめてロードする

Last updated at Posted at 2025-08-22

はじめに

 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);
}

実行結果
複数の画像を選択する:

スクリーンショット 2025-08-22 101831.png

すべてロードされる。ファイルはオブジェクトなどで別途管理する必要がある。

スクリーンショット 2025-08-22 101858.png

今回はロードして並べるだけ。

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を使って書き直すことを提案されて、理解が不十分なんですが一応書き直してみました。

index.html
<!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>
mySketch.js
// 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();
}
style.css
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と組み合わせて使うのも楽しそうです。以上です。

スクリーンショット 2025-08-24 002941.png

もともとこのコードは自サイトでロードした画像をFFmpegで圧縮してキャンバスに落としてダウンロードするのをやりたくて用意したのでした(随時修正中)。

圧縮前(4608x3456, 3.80MB)

DSCN4218.JPG

圧縮後(1920x1440, 400KB)

output_35703.jpg

2
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?