【JavaScript】ファイルダウンロードの方法を初心者向けに解説

はじめに

Webアプリケーションやウェブサイトで、ボタンをクリックするとファイルをダウンロードさせたいと考える場面は多いのではないでしょうか。

たとえば、ユーザーにPDFドキュメントを提供したり、CSVファイルを生成してダウンロードさせたりといった用途が挙げられます。

JavaScriptはブラウザ上で動作し、ユーザーがすぐに利用できる言語です。

そのため、特別な環境準備をせずとも簡単に試してみることができます。

この記事では、JavaScriptでファイルをダウンロードする方法について、具体的なコード例を用いながら解説します。

実務でよくあるシーンや、トラブルを回避するための注意点なども交えて説明します。

初心者の方でも無理なく理解できるように、わかりやすい表現を心がけていますので、気軽に読み進めてみてください。

この記事を読むとわかること

  • JavaScriptを使ったファイルダウンロードの基本的な仕組み
  • BlobやURL.createObjectURLを活用する方法
  • フェッチAPIなどを活用してサーバーから取得したファイルをダウンロードさせる方法
  • 実務で役立つ応用例や注意点

ここからは、ダウンロードの基本概念から具体的なコードまでを順序立てて紹介していきます。

JavaScriptでファイルをダウンロードするとは

JavaScriptを使ってファイルをダウンロードするとは、ユーザーがクリックなどのアクションを行ったときに、あらかじめ用意してあるデータやサーバーから取得したデータをファイルとしてブラウザに落としてもらう仕組みを指します。

多くの場合、ユーザーの操作によってダウンロードが開始されるため、任意のタイミングでファイルを生成したり、サーバーとの通信結果をもとにダウンロード処理を実行したりすることができます。

実務シーンとしては、以下のような用途が考えられます。

  • 顧客データや売上データをCSV形式でエクスポート
  • 特定の情報をPDFファイルとして閲覧・保存
  • 画像などのリソースをまとめてダウンロード

これらの機能を用意しておくことで、ユーザーにとって使いやすいサービスを提供できます。

ファイルダウンロードの基本的な仕組み

JavaScriptでは、クリックした瞬間に自動でファイル保存ダイアログを開いたり、ブラウザの既定の保存場所にファイルを保存させたりできます。

一番シンプルな方法は、HTMLの<a>タグに download属性 を付与する手段です。

ただし、実務では動的なデータを生成してダウンロードさせたい場合も多いです。

そのときに活躍するのが、BlobオブジェクトURL.createObjectURL です。

これらを組み合わせて使うことで、任意のバイナリデータやテキストデータをファイルとしてダウンロードさせられます。

download属性を使った簡単なダウンロード方法

まずは基本として、HTMLファイルの中に直接リンクを配置し、download属性を設定する方法を見てみましょう。

一番単純な例として、以下のようなコードが考えられます。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
</head>
<body>
  <a href="sample.txt" download="mySample.txt">テキストファイルをダウンロード</a>
</body>
</html>

ここで download="mySample.txt" と指定すると、クリックした際にmySample.txtという名前でダウンロードが開始されます。

これはあらかじめ用意されているファイルをダウンロードさせるときには便利です。

しかし、ユーザーの操作によってファイルを動的に作り、ダウンロードまで実行したいときには少し工夫が必要になります。

Blobを使う方法

JavaScriptの Blob オブジェクトを使うと、ブラウザ上で任意のバイナリデータやテキストデータをまとめ、それをファイル形式に見立てることが可能です。

Blobは「Binary Large Object」の略称で、データの塊を扱うために用意されています。

Blob生成の流れ

  1. 文字列や配列を用いてBlobオブジェクトを作成
  2. ブラウザの機能URL.createObjectURL(blob)で、Blobを指す一時的なURL(オブジェクトURL)を取得
  3. <a>タグのhref属性にそのURLを指定し、download属性を設定
  4. ユーザーの操作でダウンロード開始

実際のコード例を見ていきましょう。

Blobオブジェクトのサンプルコード

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
</head>
<body>
  <button id="downloadBtn">ファイルをダウンロード</button>

  <script>
    const downloadBtn = document.getElementById("downloadBtn");

    downloadBtn.addEventListener("click", () => {
      // ダウンロードさせたい文字列を準備
      const data = "こんにちは、これはダウンロード用のテキストです。";

      // Blobオブジェクトを生成
      const blob = new Blob([data], { type: "text/plain" });

      // 一時的にアクセスできるオブジェクトURLを作成
      const blobUrl = URL.createObjectURL(blob);

      // ダウンロード用リンクを動的に作成
      const link = document.createElement("a");
      link.href = blobUrl;
      link.download = "sample.txt";

      // リンクをクリックしてダウンロードを実行
      link.click();

      // メモリリークを防ぐため、URL.revokeObjectURLでURLを破棄することも検討
      URL.revokeObjectURL(blobUrl);
    });
  </script>
</body>
</html>

ここでは text/plain というMIMEタイプを指定しているので、テキストファイルとして認識されます。

実際の運用では、画像やPDF、CSV、JSONなど、さまざまな形式に応じてMIMEタイプを変えることが多いです。

サーバーから取得したファイルをダウンロードさせる方法

実務では、サーバーに置いてあるファイルをダウンロードするケースが一般的です。

サーバーからデータを受け取り、それを利用者にダウンロードさせる場合、fetch API(あるいは他のライブラリなど)を使います。

そして取得したデータをBlobとして扱い、先ほどの手順でダウンロードリンクを作る流れです。

fetch APIとBlobの活用例

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
</head>
<body>
  <button id="downloadImageBtn">画像をダウンロード</button>

  <script>
    const button = document.getElementById("downloadImageBtn");

    button.addEventListener("click", async () => {
      try {
        // サーバーのURLにGETリクエストを送信
        const response = await fetch("https://example.com/images/sample.png");

        // レスポンスをBlobとして取得
        const blobData = await response.blob();

        // オブジェクトURLを生成
        const blobUrl = URL.createObjectURL(blobData);

        // ダウンロード用のリンクを作成
        const link = document.createElement("a");
        link.href = blobUrl;
        link.download = "downloaded_image.png";
        link.click();

        // 不要になったURLは破棄することが望ましい
        URL.revokeObjectURL(blobUrl);
      } catch (error) {
        console.error("ダウンロードエラー: ", error);
      }
    });
  </script>
</body>
</html>

このように、サーバーから取得する処理を加えるだけで、ダウンロード対象ファイルが動的に変えられます。

画像以外にも、PDFやCSVなど用途に応じてURLを切り替えればOKです。

実務での活用シーンと注意点

ここまで紹介した手法は便利ですが、実際に業務システムなどで利用する場合には、以下のようなポイントにも気をつけるとよいでしょう。

1) ファイルサイズの大きさ

大容量のファイルをブラウザ上で取得しようとすると、メモリの負荷が増してしまいます。

ユーザーの回線速度によっては、ダウンロードに時間がかかったり、途中で通信が途切れたりする恐れがあります。

特にBlobを扱う場合、ブラウザが一時的に多くのメモリを消費することもあるので、ファイルサイズの上限や分割ダウンロードを考慮する必要があります。

2) ファイル名や拡張子

download属性で指定したファイル名が、ブラウザによってはそのまま反映されない場合があります。

また、ファイル名を日本語にすると一部の環境で文字化けを起こすケースもあるので注意が必要です。

拡張子も、ユーザーが開くときに混乱しないように実データに合ったものを設定します。

3) セキュリティ上のリスク

サーバーから取得したファイルをそのままダウンロードする場合、権限のないユーザーが直接ダウンロードURLを知ってしまうリスクが考えられます。

必要に応じて認証や権限管理を適切に行い、ダウンロードリンクを無闇に外部に知られないように設計しましょう。

また、Blobを生成する処理でも、ユーザーが意図しないファイルがダウンロードされることがないようにバリデーションを行うと安心です。

4) ブラウザの互換性

一般的なモダンブラウザでは、URL.createObjectURLfetchが利用できます。

一方で、古いブラウザをサポートしなければいけないプロジェクトの場合は、別の方法やポリフィルを検討する必要があります。

実務環境で利用する際は、想定ユーザーが使うブラウザのバージョンを確認しておきましょう。

Blobを使わずにBase64を利用する方法

JavaScriptでファイルのダウンロードを実装するとき、時にはBase64形式でエンコードしたデータを使用する場面もあります。

画像やPDFをBase64文字列に変換し、それを<a>タグに埋め込むことでダウンロードを実行可能です。

しかし、Base64はデータが文字列として扱われるため、サイズが大きくなるというデメリットがあります。

大容量ファイルを扱うときは非効率な場合が多いため、小さなファイルや特別な用途に限定して使われることが一般的です。

CSVファイルダウンロードの例

実務で特に多いのが、レポートデータや顧客リストなどをCSV形式にしてユーザーにダウンロードしてもらうケースです。

サンプルコードを見てみましょう。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
</head>
<body>
  <button id="csvDownloadBtn">CSVダウンロード</button>

  <script>
    const csvDownloadBtn = document.getElementById("csvDownloadBtn");

    csvDownloadBtn.addEventListener("click", () => {
      // CSVデータを文字列で用意
      const csvContent = [
        ["名前","年齢","出身地"],
        ["田中 太郎","30","東京"],
        ["山田 花子","25","大阪"]
      ];

      // CSV文字列に変換
      const csvRows = csvContent.map(row => row.join(",")).join("\n");

      // Blobにまとめる
      const blob = new Blob([csvRows], { type: "text/csv" });
      const url = URL.createObjectURL(blob);

      // ダウンロードを実行
      const link = document.createElement("a");
      link.href = url;
      link.download = "sample.csv";
      link.click();
      URL.revokeObjectURL(url);
    });
  </script>
</body>
</html>

このように配列やオブジェクトから文字列を組み立てるだけで、簡単にCSVファイルのエクスポートが可能です。

業務で使うデータを整形し、必要な項目をまとめてCSVとして出力すれば、ユーザーがExcelなどで開いて編集できるメリットがあります。

ダウンロードの進行状況をユーザーに見せるには

ファイルが大きい場合や通信速度が遅い場合、ダウンロードに時間がかかることがあります。

そのとき、ユーザーに進捗状況を知らせたいケースもあるでしょう。

たとえば、fetch APIの中でも ReadableStream を扱って受信データのサイズを追跡すれば、進捗バーのようなUIを実装できます。

ただし、初心者の方が最初から実装するには少し難易度が高いかもしれません。

無理のない範囲で、可能な部分から学んでいくとよいでしょう。

複数ファイルをまとめてダウンロードしたい場合

複数のファイルを一度にダウンロードさせたい場合、zipライブラリを使ってブラウザ上でzipファイルを生成し、ダウンロードさせる方法があります。

複数のBlobをまとめて一つの圧縮ファイルにしてしまうと、ユーザーとしては分かりやすい形で一括ダウンロードができます。

例としては、JavaScriptのzip操作に対応したライブラリを使い、生成したzipをBlobに変換してダウンロードする流れです。

ただ、複雑な処理やライブラリに依存したコードになる場合が多いので、プロジェクトの要件に合うかどうかを検討してみてください。

よくあるトラブルと対処法

ダウンロードされずブラウザ上で開いてしまう

PDFなどで発生しやすいのですが、ダウンロードの代わりにブラウザのビューワが開く場合があります。

これはブラウザやデフォルト動作が影響しているケースもあるため、確実にダウンロードさせたい場合には、サーバー側のレスポンスヘッダにContent-Disposition: attachmentを付与する手段を検討することがあります。

フロントエンドだけで解決できないこともあるので、サーバーサイドの設定も考慮しましょう。

ダウンロードファイルの文字化け

CSVファイルで日本語の文字化けが起きる場合は、ファイルのエンコードに注意が必要です。

一般的にUTF-8で統一すれば問題なく扱える場面が多いですが、環境によってはShift-JISなど別のエンコードを要求されることもあります。

フロントエンドでBlob生成する場合も、適切なエンコードに合わせて文字列を生成したり、BOM付きUTF-8にしたりと工夫が必要な場合があります。

文字化け対策としては、「BOMを付けたUTF-8形式」にするなどの方法がよく採用されます。

実務で考慮するとよいポイント

ここまで紹介してきた方法を踏まえて、実務でファイルダウンロードの機能を提供するときに押さえておきたいポイントをいくつか紹介します。

プログレス表示の検討

大きなファイルを扱う場合、途中経過がわからないとユーザーが混乱しがちです。

進行状況を示すプログレスバーやステータス表示を用意してあげると、利用者のストレスが軽減します。

エラー処理

ネットワーク障害やサーバーダウンなどに備えて、ダウンロードに失敗した場合のエラー表示を明確にしておくと良いでしょう。

たとえば、「通信エラーが発生しました」などのエラーメッセージをユーザーに伝えることで、データを再取得するかどうかの判断がしやすくなります。

ダウンロード完了の通知

ユーザーがダウンロード完了を見落とさないように、完了したタイミングでメッセージやトースト通知を出すことがあります。

特に複数ファイルを順番にダウンロードする場合、どこまで処理が進んだかをわかりやすく伝えることが大切です。

ファイルダウンロードの実装は機能テストだけではなく、ネットワーク速度が遅い環境やモバイル回線などさまざまな状況を想定した検証が必要です。

まとめ

JavaScriptを使ったファイルのダウンロードは、ユーザーにとって実用性の高い機能のひとつです。

基本的には<a>タグのdownload属性とBlob、あるいはサーバーからのデータ取得と組み合わせることで、多彩なダウンロード機能を実装できます。

具体的な流れとしては、Blobを作成してURLを生成し、リンクを自動でクリックさせるという仕組みがカギになります。

実務では、ファイルサイズの制約やエンコードの問題、ブラウザの対応状況、セキュリティなど注意すべき点が少なくありません。

しかし、ひとつずつ押さえておくことで、ユーザーが快適にデータを扱える仕組みを整えられます。

今回紹介したサンプルコードを参考にして、自分のプロジェクトやサービスに合わせたダウンロード機能を実装してみてください。

シンプルなテキストファイルや小さめの画像から慣れると取り組みやすいかもしれませんね。
慣れてくればCSVダウンロードやPDF、さらには複数ファイルをzipにまとめてダウンロードするなど、用途に応じた機能拡張も視野に入れられるでしょう。

JavaScriptをマスターしよう

この記事で学んだJavaScriptの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。