JavaScript | Web API:ファイル・データ操作 - ダウンロード処理

JavaScript JavaScript
スポンサーリンク

「ダウンロード処理」は“ブラウザの中のデータ”を“ユーザーのファイル”に変えること

JavaScript でいう「ダウンロード処理」は、
ブラウザの中にあるデータ(文字列・JSON・画像・Blob など)を、ユーザーにファイルとして保存させること です。

サーバーから来たデータをそのまま保存させることもあれば、
ブラウザ内で生成したテキストや画像、集計結果などを
「ユーザーの手元にファイルとして渡す」こともあります。

ここでよく使う道具は主に 3 つです。

Blob(データのかたまりを表す)
URL.createObjectURL(Blob に一時的な URL を貼る)
<a download>(リンクを「保存ダイアログ」に変える)

この 3 つを組み合わせると、
「ブラウザ内のデータ → ダウンロードファイル」という流れを自在に作れます。


一番基本のパターン:文字列 → Blob → createObjectURL → a.download

文字列をテキストファイルとしてダウンロードさせる

まずは、最もシンプルで強力なパターンから。

HTML:

<button id="download">テキストをダウンロード</button>

JavaScript:

const btn = document.querySelector("#download");

btn.addEventListener("click", () => {
  const text = "こんにちは\nこれはダウンロード用に作ったテキストです。";

  const blob = new Blob([text], { type: "text/plain" });

  const url = URL.createObjectURL(blob);

  const a = document.createElement("a");
  a.href = url;
  a.download = "sample.txt";
  a.click();

  URL.revokeObjectURL(url);
});
JavaScript

ここで起きていることを順番に分解すると、こうなります。

ブラウザ内の文字列 text を Blob に変換する
Blob に対して URL.createObjectURL で一時 URL を発行する
<a> 要素を作り、その href にその URL をセットする
a.download = "sample.txt" で「保存時のファイル名」を指定する
a.click() で擬似的にクリックして、ブラウザに「ダウンロード開始」をさせる
使い終わった URL を URL.revokeObjectURL(url) で解放する

ここでの超重要ポイントは、

「ブラウザ内のデータを“ファイルとして保存させる”には、
一度 Blob にして、URL を発行し、<a download> に渡す」

という流れを作る、ということです。


JSON や CSV など「アプリのデータ」をダウンロードする

JSON オブジェクトを data.json として保存させる

HTML:

<button id="download-json">JSON をダウンロード</button>

JavaScript:

const btn = document.querySelector("#download-json");

btn.addEventListener("click", () => {
  const data = {
    name: "太郎",
    age: 25,
    skills: ["JavaScript", "HTML", "CSS"]
  };

  const json = JSON.stringify(data, null, 2);

  const blob = new Blob([json], { type: "application/json" });

  const url = URL.createObjectURL(blob);

  const a = document.createElement("a");
  a.href = url;
  a.download = "data.json";
  a.click();

  URL.revokeObjectURL(url);
});
JavaScript

ここでのポイントは、

アプリ内のオブジェクトを JSON 文字列に変換する
JSON 文字列から Blob を作る
あとはさっきと同じ「Blob → URL → a.download」の流れ

ということです。

「アプリの状態をエクスポートする」
「設定を JSON として保存させる」
といった機能は、このパターンで実現できます。

CSV も同じノリでいける

例えば、配列の配列を CSV にしてダウンロードする場合も同じです。

const rows = [
  ["name", "age"],
  ["太郎", 25],
  ["花子", 22]
];

const csv = rows.map(cols => cols.join(",")).join("\n");

const blob = new Blob([csv], { type: "text/csv" });
// あとは同じ:createObjectURL → a.download
JavaScript

「どんな形式のデータでも、最終的には文字列にして Blob に入れればファイルにできる」
という感覚を持っておくと、応用が効きます。


画像やバイナリのダウンロード:Blob をそのまま使う

Canvas から画像ファイルとして保存させる

Canvas と組み合わせると、「描いたものを画像として保存」ができます。

HTML:

<canvas id="canvas" width="200" height="200"></canvas>
<button id="save">画像として保存</button>

JavaScript:

const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext("2d");
const btn = document.querySelector("#save");

ctx.fillStyle = "skyblue";
ctx.fillRect(0, 0, 200, 200);
ctx.fillStyle = "red";
ctx.fillRect(50, 50, 100, 100);

btn.addEventListener("click", () => {
  canvas.toBlob((blob) => {
    if (!blob) return;

    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "canvas.png";
    a.click();
    URL.revokeObjectURL(url);
  }, "image/png");
});
JavaScript

ここでは、

canvas.toBlob で Canvas の内容を画像 Blob に変換
その Blob を createObjectURL で URL にする
a.download でファイル名を指定してダウンロード

という流れです。

「ブラウザで描いたものを、そのままユーザーのファイルにする」
というのは、ダウンロード処理のとても分かりやすい応用例です。

サーバーから fetch したバイナリをそのまま保存させる

サーバーからファイルを取得して、そのままユーザーに保存させることもできます。

async function downloadFromServer() {
  const res = await fetch("/api/report");
  const blob = await res.blob();

  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = "report.pdf";
  a.click();
  URL.revokeObjectURL(url);
}
JavaScript

ここでは、

fetch でレスポンスを受け取る
res.blob() で中身を Blob として取得
あとは同じく「Blob → URL → a.download」

という流れです。

「サーバー側はファイルを返すだけ、
クライアント側はそれをダウンロードさせる UI を持つ」
という役割分担ができます。


重要ポイント:download 属性と「通常のリンク」との違い

通常のリンクは「開く」、download は「保存させる」

普通のリンクはこうです。

<a href="/files/manual.pdf">マニュアル</a>

クリックすると、
ブラウザはその URL を開こうとします(PDF ビューアなど)。

一方、download 属性を付けると、
「開く」ではなく「保存させる」方向に動きます。

<a href="/files/manual.pdf" download="manual.pdf">マニュアルをダウンロード</a>

これを JavaScript で動的に作っているのが、
さっきの a.download = "xxx" のコードです。

download 属性は、
「このリンクは“開く”というより“保存させたい”」
という意図をブラウザに伝えるスイッチだと捉えてください。

サーバー側の Content-Disposition との関係

実務では、サーバー側で
Content-Disposition: attachment; filename="..."
というヘッダを付けて「ダウンロードさせる」ことも多いです。

クライアント側で download 属性を使うか、
サーバー側でヘッダを付けるか、
あるいは両方使うかは設計次第ですが、
「クライアント側だけでも、Blob と download 属性でかなりのことができる」
というのは覚えておいて損はありません。


重要ポイント:メモリと URL.revokeObjectURL の話

createObjectURL は「ブラウザに URL と Blob の対応を覚えさせる」

URL.createObjectURL(blob) を呼ぶと、
ブラウザは内部的に「この URL が来たらこの Blob を返す」という対応を保持します。

これ自体は便利ですが、
大量に作って放置するとメモリを食い続けます。

そこで、
「もうこの URL は使わない」タイミングで URL.revokeObjectURL(url) を呼ぶ
という習慣をつけておくと安心です。

いつ解放すればいいか

ダウンロード用の URL:

a.click() でダウンロードを開始した直後に revoke して OK
ユーザーのダウンロードは既に始まっているので問題ない

プレビュー用の URL:

その URL を参照している <img><a> を DOM から消したタイミングで revoke
プレビューを更新するときに、古い URL をまとめて revoke

「参照している要素がもうないなら、URL も要らない」
という感覚で考えると分かりやすいです。


初心者として「ダウンロード処理」で本当に掴んでほしいこと

ブラウザ内のデータをファイルとして保存させるには、
基本的にこの流れを踏みます。

データ(文字列・JSON・バイナリ)を用意する
それを Blob に包む(new Blob([...], { type })
URL.createObjectURL(blob) で一時 URL を作る
<a> 要素の href にその URL を入れ、download でファイル名を指定する
a.click() でダウンロードを開始し、使い終わった URL を revoke する

まずは次の 2 つを、自分の手で書いてみてください。

テキストを sample.txt としてダウンロードさせるボタン
JSON オブジェクトを data.json としてダウンロードさせるボタン

この 2 つがスムーズに書けるようになったとき、
「ダウンロード処理」は単なるおまけ機能ではなく、
「Web アプリの中のデータを、ユーザーの手元にちゃんと返すための出口」
として、かなり頼もしい存在に見えてくるはずです。

タイトルとURLをコピーしました