【JavaScript】ファイルをダウンロードする方法|Blob URL・CSV / JSON 生成・fetch + Blob・ファイル名指定・CORS 対策まで解説

CSV エクスポート、レポートの PDF ダウンロード、Canvas の画像保存など、JavaScript でファイルをダウンロードさせる機能は多くの Web アプリで必要になります。この記事では a タグの download 属性による基本から、Blob URL を使った動的ファイル生成、fetch でサーバーのファイルを取得してダウンロードする方法まで解説します。

この記事でわかること
・a タグの download 属性による基本的なダウンロード
・Blob URL で動的生成データをダウンロードする方法
・CSV / JSON / テキストファイルの生成とダウンロード
・fetch + Blob でサーバー上のファイルをダウンロード
・ファイル名の指定方法
・クロスオリジン(CORS)の制限と対策
・URL.revokeObjectURL によるメモリ解放
・ダウンロードボタンの実務パターン
スポンサーリンク

ダウンロード方法の比較

方法 データソース ファイル名指定 CORS
a タグ + download 属性 同一オリジンの URL 同一オリジンのみ
Blob URL JavaScript で生成したデータ 不要(クライアントで生成)
fetch + Blob サーバー上のファイル サーバー側で許可が必要

a タグの download 属性でダウンロードする(基本)

HTML の a タグに download 属性を付けるだけで、リンクをクリックしたときにファイルがダウンロードされます。

HTML
<!-- download 属性でファイル名を指定 -->
<a href="/files/report.pdf" download="レポート.pdf">PDFをダウンロード</a>

<!-- download 属性のみ(ファイル名はサーバーのファイル名) -->
<a href="/files/data.csv" download>CSVをダウンロード</a>

JavaScript でボタンクリック時にダウンロードする

JavaScript
function downloadFromUrl(url, filename) {
  const a = document.createElement("a");
  a.href = url;
  a.download = filename || "";
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}

// 使用例
document.getElementById("downloadBtn").addEventListener("click", () => {
  downloadFromUrl("/files/report.pdf", "レポート.pdf");
});
download 属性は同一オリジンの URL でのみ有効です。クロスオリジン(別ドメイン)の URL では download 属性が無視され、ブラウザの通常のナビゲーション(ページ遷移や新しいタブで開く)が行われます。

Blob URL で動的生成データをダウンロードする

JavaScript で生成したデータ(テキスト・CSV・JSON など)をダウンロードするには、Blob を作成して URL.createObjectURL() で一時的な URL を生成します。

テキストファイルを生成してダウンロード
function downloadText(text, filename) {
  const blob = new Blob([text], { type: "text/plain" });
  const url = URL.createObjectURL(blob);

  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();

  // メモリ解放
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

// 使用例
downloadText("Hello, World!", "hello.txt");
URL.revokeObjectURL(url) を呼んで Blob URL を解放してください。解放しないと Blob のメモリが保持され続けます。

CSV ファイルを生成してダウンロードする

JavaScript
function downloadCSV(data, filename = "data.csv") {
  // ヘッダー行
  const headers = Object.keys(data[0]);
  const csvRows = [
    headers.join(","),
    ...data.map(row =>
      headers.map(h => {
        const val = String(row[h] ?? "");
        // カンマや改行を含む場合はダブルクォートで囲む
        return val.includes(",") || val.includes("\n")
          ? `"${val.replace(/"/g, '""')}"`
          : val;
      }).join(",")
    )
  ];

  // BOM 付き UTF-8(Excel で文字化けしない)
  const bom = "\uFEFF";
  const blob = new Blob([bom + csvRows.join("\n")], { type: "text/csv" });
  const url = URL.createObjectURL(blob);

  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
}

// 使用例
const users = [
  { name: "田中", age: 30, city: "東京" },
  { name: "鈴木", age: 25, city: "大阪" },
  { name: "佐藤", age: 35, city: "名古屋" }
];
downloadCSV(users, "ユーザー一覧.csv");
CSV を Excel で開くと日本語が文字化けすることがあります。先頭に BOM(\uFEFF) を付けることで Excel が UTF-8 として正しく認識します。

JSON ファイルを生成してダウンロードする

JavaScript
function downloadJSON(data, filename = "data.json") {
  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 = filename;
  a.click();
  URL.revokeObjectURL(url);
}

// 使用例
downloadJSON({ name: "田中", age: 30 }, "user.json");

fetch + Blob でサーバー上のファイルをダウンロードする

サーバー上のファイルを JavaScript 経由でダウンロードしたい場合(ファイル名を動的に変更したい、認証ヘッダーを付けたい場合など)は、fetch で取得して Blob に変換します。

JavaScript
async function downloadFile(url, filename) {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  const blob = await res.blob();
  const blobUrl = URL.createObjectURL(blob);

  const a = document.createElement("a");
  a.href = blobUrl;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(blobUrl);
}

// 使用例: APIから認証付きでファイルを取得
async function downloadReport() {
  const res = await fetch("/api/report", {
    headers: { Authorization: "Bearer " + token }
  });
  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);
}
fetch を使えば認証ヘッダー(Bearer トークンなど)を付けてファイルを取得できます。a タグの download 属性だけでは認証が必要なファイルはダウンロードできません。

クロスオリジン(CORS)の制限と対策

状況 download 属性 fetch + Blob
同一オリジン ○ 有効 ○ 動作する
クロスオリジン(CORS 許可あり) × 無視される ○ 動作する
クロスオリジン(CORS 許可なし) × 無視される × エラー

別ドメインのファイルをダウンロードしたい場合は、fetch + Blob を使い、サーバー側で Access-Control-Allow-Origin ヘッダーを設定してもらう必要があります。

クロスオリジンの制限はブラウザのセキュリティ機能です。CORS ヘッダーの設定はサーバー側の管理者に依頼してください。

汎用ダウンロード関数

Blob URL 生成・a タグ作成・クリック・メモリ解放を共通化した汎用関数です。

JavaScript
function download(blob, filename) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

// テキスト
download(new Blob(["Hello"], { type: "text/plain" }), "hello.txt");

// CSV
download(new Blob(["\uFEFFname,age\n田中,30"], { type: "text/csv" }), "data.csv");

// JSON
download(new Blob([JSON.stringify(data)], { type: "application/json" }), "data.json");

実務でよく使うパターン

Canvas の画像をダウンロードする

JavaScript
const canvas = document.getElementById("myCanvas");

document.getElementById("saveBtn").addEventListener("click", () => {
  canvas.toBlob(blob => {
    download(blob, "drawing.png");
  }, "image/png");
});

テーブルデータを CSV エクスポートする

JavaScript
function exportTableToCSV(tableId, filename = "table.csv") {
  const table = document.getElementById(tableId);
  const rows = [...table.querySelectorAll("tr")];

  const csv = rows.map(row =>
    [...row.querySelectorAll("th, td")]
      .map(cell => `"${cell.textContent.replace(/"/g, '""')}")`)
      .join(",")
  ).join("\n");

  download(
    new Blob(["\uFEFF" + csv], { type: "text/csv" }),
    filename
  );
}

document.getElementById("exportBtn").addEventListener("click", () => {
  exportTableToCSV("dataTable", "エクスポート.csv");
});

ダウンロードボタンにローディング表示を付ける

JavaScript
const btn = document.getElementById("downloadBtn");

btn.addEventListener("click", async () => {
  btn.disabled = true;
  btn.textContent = "ダウンロード中...";

  try {
    const res = await fetch("/api/export");
    const blob = await res.blob();
    download(blob, "export.xlsx");
  } catch (err) {
    alert("ダウンロードに失敗しました");
  } finally {
    btn.disabled = false;
    btn.textContent = "ダウンロード";
  }
});

関連記事

よくある質問

Qdownload 属性が効きません。ファイル名が指定されずにブラウザで開いてしまいます。
Adownload 属性は同一オリジンの URL でのみ有効です。別ドメインの URL では無視されます。クロスオリジンのファイルをダウンロードするには fetch + Blob を使ってください。
QCSV をダウンロードすると Excel で文字化けします。
ACSV の先頭に BOM(\uFEFF) を付けてください。new Blob(["\uFEFF" + csv], { type: "text/csv" }) とすることで、Excel が UTF-8 として正しく認識します。
QURL.revokeObjectURL を呼ばないとどうなりますか?
ABlob URL が解放されず、Blob のメモリが保持され続けます。小さなファイルなら問題になりませんが、大きなファイルや繰り返しダウンロードする場面ではメモリリークの原因になります。ダウンロード後に必ず呼んでください。
Qfetch で認証が必要なファイルをダウンロードするには?
Afetch の headers オプションに認証情報(Bearer トークンなど)を渡します。レスポンスを res.blob() で Blob に変換し、URL.createObjectURL でダウンロードします。
QCanvas の描画内容をダウンロードするには?
Acanvas.toBlob(blob => { ... }) で Blob を取得し、URL.createObjectURL で a タグにセットしてダウンロードします。PNG なら "image/png"、JPEG なら "image/jpeg" を指定します。

まとめ

JavaScript でファイルをダウンロードする方法を整理しました。

  • 同一オリジンの URL: a タグ + download 属性(最もシンプル)
  • 動的生成データ: Blob + URL.createObjectURL(CSV / JSON / テキスト)
  • サーバー上のファイル: fetch + res.blob()(認証付き・CORS 対応)
  • メモリ解放: URL.revokeObjectURL を忘れずに
  • Excel 文字化け対策: CSV の先頭に BOM(\uFEFF)を付ける

CSV エクスポートや Canvas 画像の保存は実務で頻出するパターンです。汎用の download 関数を用意しておくと、さまざまな場面で再利用できます。