0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

複数の画像ファイルを順番に一括処理する

0
Posted at

やりたいこと

前回までで1枚の画像をリサイズして返す仕組みができた。
次は複数ファイルをまとめて処理したい。

やり方はいくつかあって、全部並列で投げる方法と、
1枚ずつ順番に処理する方法があります。

並列の方が速そうに見えるけど、画像が多いと一気にメモリを食うので
実用的には順番に処理する方が安定することが多いです。


コード

const worker = new Worker('worker.js');  // 前回と同じ worker.js を使う

async function processFiles(files) {
  const results = [];

  for (const file of files) {
    const result = await processOne(file);
    results.push(result);

    // 1枚終わるたびに進捗を更新
    updateProgress(results.length, files.length);
  }

  return results;
}

function processOne(file) {
  return new Promise(async (resolve) => {
    const bitmap = await createImageBitmap(file);

    worker.onmessage = (e) => resolve({ ...e.data, name: file.name });
    worker.postMessage(
      { bitmap, maxWidth: 800, maxHeight: 800 },
      [bitmap]
    );
  });
}

function updateProgress(done, total) {
  progress.textContent = `${done} / ${total} 完了`;
}

HTML 側

<input type="file" id="input" accept="image/*" multiple />
<p id="progress"></p>
<div id="results"></div>

<script>
  const input    = document.getElementById('input');
  const progress = document.getElementById('progress');
  const results  = document.getElementById('results');

  input.addEventListener('change', async (e) => {
    const files = Array.from(e.target.files);
    progress.textContent = `0 / ${files.length} 完了`;

    const processed = await processFiles(files);

    processed.forEach(({ blob, name, width, height }) => {
      const url = URL.createObjectURL(blob);
      const a   = document.createElement('a');
      a.href     = url;
      a.download = `resized_${name}`;
      a.textContent = `${name}${width}×${height})`;
      results.appendChild(a);
      results.appendChild(document.createElement('br'));
    });
  });
</script>

for...of で回す理由

files.forEachasync を渡しても、中の await が正しく効かないので
直列処理にならないことがあります。

for...of の中で await を使うと、1枚の処理が終わってから
次に進む直列処理になるので、メモリの使われ方が予測しやすいです。


試してみてわかったこと

20〜30枚くらいまでは特に問題なく動きました。
100枚以上になってくると、処理自体は動くけど
ダウンロードリンクが増えすぎて画面が見づらくなったので
ZIPにまとめて落とせるようにした方がよさそうだと感じています。

そのあたりは次以降で触れる予定です。

次は出力フォーマットの話で、WebP と AVIF への変換を書きます。

自分でゼロから実装するのが大変な場合は、
同じ仕組みで動くブラウザツール BulkPicTools も作っているので、
よかったら使ってみてください → https://bulkpictools.com

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?