ブラウザからファイル出力(ダウンロード処理)

ピュアなJavaScriptを使ってブラウザからファイルを出力させる方法です。この方法を使えば、JavaScriptによってブラウザ上で生成された任意のデータ(例えばCSVとか)をファイルとしてダウンロードさせることができます。

目次

前提

  • Chromeブラウザ、Firefoxなどのモダンブラウザのみ対応 (IEは知りません)

コード

ダウンロード処理のコードはこちら:

/**
 * ダウンロード処理
 * @param content {String} - ダウンロードさせたいデータ
 * @param filename {String} - ダウンロードさせるときのファイル名
 */
function download(content, filename) {
  // create a temporary "a" element.
  const a = document.createElement("a");
  document.body.appendChild(a);
  a.style = "display:none";
  const blob = new Blob([content], { type: "octet/stream" });
  const url = window.URL.createObjectURL(blob);
  a.href = url;
  a.download = filename;
  a.click();
  window.URL.revokeObjectURL(url); // release the used object.
  a.parentNode.removeChild(a); // delete the temporary "a" element
}

コードレビュー

上記のdownload()内部では以下の処理が行われます:

  1. 目に見えないa要素を一時的に作成
  2. データ内容をblob化し、更にdataURI化 (例:blob:null/1a378260-d62f-45c5-a4b5-561093948194)
  3. a要素のhref属性にデータ内容のdataURIをセット
  4. a要素をクリック (ダウンロード実行)
  5. 作成したデータとa要素の掃除

使用例

HTMLに組み込むと以下のような感じになります。ダウンロードボタンを押すとあいうえお,かきくけこという内容のCSVファイルtest.csvをダウンロードします:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>ダウンロードテスト</title>
</head>

<body>
  <button onclick="download('あいうえお,かきくけこ', 'test.csv')">ダウンロード</button>
  <script type="text/javascript">
  function download(content, filename) {
    // create a temporary "a" element.
    const a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display:none";
    const blob = new Blob([content], { type: "octet/stream" });
    const url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = filename;
    a.click();
    window.URL.revokeObjectURL(url); // release the used object.
    a.parentNode.removeChild(a); // delete the temporary "a" element
  }
  </script>
</body>

</html>

TIPS

Excel でも文字化けせずに開けるCSVファイルを出力する方法

あまり本質とは関係ないですが、先ほどのコードで出力されたCSVファイルは Excel では文字化けしてうまく開けません。(2019/06/21時点)

その理由として、上記のコードではファイルのエンコードはUTF-8としてダウンロードされるのですが、Excelは純粋な(BOM無しの)UTF-8ファイルに対応していないためです。しかし、回避方法はあります。それは先ほどのCSVデータの先頭にBOMを付けることです:

...

// Excel 対応
// 先頭にBOM付与:
content = "\ufeff" + content;

const blob = new Blob([content], { type: "octet/stream" });
...

余談: マイクロソフトが近いうちに純粋なUTF-8ファイルに対応させるという情報をどこかで見た記憶がありますが、ソースは忘れてしまいました。あまりにも興味が無さ過ぎて😩

芽萌丸プログラミング部 @programming
プログラミング関連アカウント。Web標準技術を中心に書いていきます。フロントエンドからサーバサイドまで JavaScript だけで済ませたい人たちの集いです。