はじめに
Qiita API から取得したQiitaに投稿された記事を、React で .md ファイルとしてダウンロードできるアプリを作っていましたが、
「複数チェックして一括ダウンロードしているのに、1件しか落ちない」 という問題にハマりました。
この記事では、実際に起きた症状、なぜ 1 件しかダウンロードされなかったのか(根本原因)を整理して共有します。
原因
結論から言うと、URL.revokeObjectURL(url) を早く呼びすぎたことが原因でした。
一括ダウンロードの処理の流れを整理すると以下のようになります。
1.記事本文から Blob を作る(Blobとはデータを入れておくための箱、ファイルの中身をJavascriptで扱うための仕組み)
2.URL.createObjectURL(blob) で “一時的なURL” を作る(URL.createObjectURL(blob)とは、Blobにアクセスするための一時的にURLを自動生成する関数)
3.a.href = url → click() でダウンロードを開始
使い終わったら URL.revokeObjectURL(url) で破棄
(URL.revokeObjectURL(url)とは、一時URLを削除する関数)
以下のコードに今回の問題点があります。
const url = URL.createObjectURL(blob);
a.href = url;
a.click();
URL.revokeObjectURL(url); // ← ここをすぐ呼んでいた
ブラウザがまだダウンロードを開始しきっていない状態で URL を破棄してしまうため、
1 件目は動いたのですが、2 件目以降の URL が無効になってしまい、結果 1 件しか落ちないという現象になっていました。
解決方法
URL.revokeObjectURL(url) を少し遅らせて呼ぶよう修正しました。
最終的に安定動作したコード
const handleDownload = (title: string, body: string) => {
const blob = new Blob([body], { type: "text/markdown" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${title}.md`;
a.click();
// すぐ破棄するとダウンロードが壊れる → 少し待つ
setTimeout(() => URL.revokeObjectURL(url), 100);
};
複数記事をチェックして一括ダウンロードしても、すべて正常に保存されるようになりました。
まとめ
適切に遅延させると複数ダウンロードが安定して動く