【JavaScript】生成データをファイルとして保存(ダウンロード)させる方法

JavaScript で何かしらのデータを生成した後、それをユーザーの PC やスマートフォンにファイルとして保存させたいときがあります。特に、CSV ファイルや JSON ファイルを保存したいケースが多いのではないでしょうか。

今回は、Chrome や Safari はもちろんのこと、スマートフォンでも生成データをファイルとしてローカルに保存する方法について解説します。

リンクをクリックしてファイルを保存する方法

a 要素のリンクをクリックすることでファイル保存させるのは比較的簡単にできます。ここでは JSON データを生成し、それをファイルとして保存できるようにします。

<a href="#" id="dllnk" download="test.json">JSON ダウンロード</a>
document.getElementById('dllnk').addEventListener('click', (event) => {
  // JSON ファイルを表す Blob オブジェクトを生成
  const json = JSON.stringify({ a: 1, b: 2, c: 3 }, null, '  ');
  const blob = new Blob([json], { type: 'application/json' });

  // a 要素の href 属性に Object URL を セット
  event.currentTarget.href = window.URL.createObjectURL(blob);
});

流れを簡単に説明すると、JSON の文字列を生成した後、その文字列を表す Blob オブジェクトを生成します。その際に MIME タイプを指定しますが、今回は JSON ファイルですので、その MIME タイプは application/json です。

Blob オブジェクトを生成したら、Blob オブジェクトの Object URL を a 要素の href 属性にセットします。これであたかもファイルをダウンロードするかのような挙動になります。

Object URL とは

Blob オブジェクトを生成したら、window.URL.createObjectURL() メソッドを使って Object URL を生成することができます。Object URL とは、該当の Blob をブラウザーが内部的に使う URL として表現した文字列です。もちろん、人が見て意味があるものではありません。実際には次のような URL が生成されます。

blob:https://webfrontend.ninja/4f2da577-6c12-45cf-9cf7-7b0a76bbfdac

ブラウザーは、この Object URL を通常の URL と同じように扱います。そのため、a 要素の href 属性に Blob オブジェクトを表す Object URL をセットすれば、あたかもサーバーからファイルをダウンロードするかのような挙動になるわけです。

Internet Explorer 11 向けの対処法

同じことを Internet Explorer 11 でも実現可能です。残念ながら Web 標準ではなく IE 独自の方法になります。以下のコードは、Chrome などのモダンブラウザーと Internet Explorer 11 のどちらでも JSON データをファイルとして保存することができます。

document.getElementById('dllnk').addEventListener('click', function (event) {
  // JSON ファイルを表す Blob オブジェクトを生成
  const json = JSON.stringify({ a: 1, b: 2, c: 3 }, null, '  ');
  const blob = new Blob([json], { type: 'application/json' });

  if (window.navigator.msSaveBlob) {
    // Internet Explorer 11 の場合
    const fname = event.currentTarget.getAttribute('download');
    window.navigator.msSaveBlob(blob, fname);
  } else {
    // Web 標準の場合
    event.currentTarget.href = window.URL.createObjectURL(blob);
  }
});

Internet Explorer 11 の場合、window.navigator.msSaveBlob() メソッドが存在しますので、それを呼び出します。msSaveBlob() メソッドは第 1 引数に Blob オブジェクトを、第 2 引数にファイル名を指定します。このメソッドが呼び出されると、指定の Blob オブジェクトのデータが指定のファイル名でファイルとして保存されます。

ボタンをクリックしてファイルを保存する方法

前述の例では a 要素の download 属性を使ってファイルを保存しましたが、button 要素のボタンのクリックでファイルとしてデータを保存したい場合は、少しやり方を変えないといけません。

<button id="dlbtn" type="button">JSON ダウンロード</button>
document.getElementById('dlbtn').addEventListener('click', () => {
  // JSON ファイルを表す Blob オブジェクトを生成
  const json = JSON.stringify({ a: 1, b: 2, c: 3 }, null, '  ');
  const blob = new Blob([json], { type: 'application/json' });

  // ダミーの a 要素を生成して body 要素の最後に追加
  let dummy_a_el = document.createElement('a');
  document.body.appendChild(dummy_a_el);

  // a 要素の href 属性に Object URL をセット
  dummy_a_el.href = window.URL.createObjectURL(blob);

  // a 要素の download 属性にファイル名をセット
  dummy_a_el.download = 'test.json';

  // 疑似的に a 要素をクリックさせる
  dummy_a_el.click();

  // a 要素を body 要素から削除
  document.body.removeChild(dummy_a_el);
});

基本的には button 要素のクリックでも、裏では a 要素を使い、冒頭のサンプルとやっていることはほとんど同じです。冒頭のサンプルとこのサンプルの違いは、初めから a 要素を用意してあるのか、あとから a 要素を生成するのかの違いしかありません。

ボタンがクリックされたら、a 要素を生成して body 要素の中に追加します。a 要素には download 属性もセットします。そして、a 要素のオブジェクトの click() メソッドを使って疑似的に a 要素をクリックさせます。そして最後に body 要素から a 要素を削除します。

画像をダウンロードさせる方法

ブラウザー上で生成して保存させたいのは JSON のようなテキストだけではありません。画像も保存させたいこともあるでしょう。とりわけ、canvas 要素に何かしらの画像を生成した場合などが該当します。または img 要素に表示された画像をファイルとして保存したい場合もあるでしょう。

次のサンプルは img 要素の画像データを PNG ファイルとして保存します。img 要素の画像データを canvas 要素に複写してから保存しますので、canvas の画像を直接保存したい場合のサンプルとしてもご覧いただけます。

<img src="pic.jpg" id="pic">
<p><a href="#" id="dlpic" download="test.png">画像ダウンロード</a></p>
document.getElementById('dlpic').addEventListener('click', (event) => {
  // canvas 要素を生成して img 要素の画像を canvas 要素に複写
  const img = document.getElementById('pic');
  const canvas = document.createElement('canvas');
  canvas.width = img.naturalWidth;
  canvas.height = img.naturalHeight;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0);

  // a 要素の href 属性に canvas の Data URL を セット
  event.currentTarget.href = canvas.toDataURL('image/png');
});

ポイントだけを言うと、canvas 要素のオブジェクトの toDataURL() メソッドを使って Data URL を生成し、それを a 要素の href 属性にセットします。

まとめ

今回紹介した JavaScript コードは、PC 向けのモダンブラウザーはもちろんのこと、iOS の Safari や Android の Chrome でも動作します。

個人的に、a 要素の download 属性を使ってファイルを保存させる Web 標準の方法は、とりわけボタンをトリガーにしたい場合は、少々トリッキーな感じがします。Internet Explorer 11 の msSaveBlob() メソッドに相当するメソッドが Web 標準にも存在すれば、自然な流れでコードが書けるでしょう。

実は、かつて W3C では File API: Writer という仕様が作られ、その中で window.saveAs() メソッドが検討されていました。使い方は Internet Explorer 11 の msSaveBlob() メソッドと同じです。しかし、最終的に File API: Writer 仕様はボツになりました。

CSS を使えば a 要素のハイパーリンクもボタンに見せかけることは可能です。そのため、ファイル保存をボタンで行いたいなら、a 要素を CSS でボタンに見せかけなさい、ということなのでしょうね。そうすれば、ユーザーにはボタンと見せかけながらも、面倒なコードを書く必要はありません。

今回は以上で終わりです。最後まで読んでくださりありがとうございました。それでは次回の記事までごきげんよう。

Share