BRIGHT VIE Advent Calendar 2018 - Qiita の4日目の記事です。
先日開発を行っている中で、JavaScriptだけでCSVを出力する仕組みを目にすることがあり、
コードの理解も含めて今日はこのことを記載できればと思います。
はじめに
下記のコードを御覧ください。
function exportCSV(records) {
let data = records.map((record)=>record.join(',')).join('\r\n');
let bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
let blob = new Blob([bom, data], {type: 'text/csv'});
let url = (window.URL || window.webkitURL).createObjectURL(blob);
let link = document.createElement('a');
link.download = 'result.csv';
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
これは、与えられたrecordsという2次元配列を、
'result.csv'というCSVファイルに出力するためのJavaScriptプログラムです。
ブラウザで下記のように実行すると
let record = [
['ID','商品名','価格'],
[1, 'りんご(箱)', 100],
[2, 'みかん (箱)', 1200]
]
exportCSV(record);
このようなCSVファイルをダウンロードすることが出来ます。
ID,商品名,価格
1,りんご(箱),100
2,みかん (箱),1200
出力の仕組みについて
上記プログラムで利用されている、Uint8Arrayで作成するbomやBlobについてまとめてみようと思います。
バイトオーダーマーク
プログラム先頭の[0xEF, 0xBB, 0xBF]は、バイトオーダマークと呼ばれるようです。
let bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
バイトオーダーマーク (byte order mark) あるいはバト順マーク(バイトじゅんマーク)は通称BOM(ボム)といわれる、Unicodeの符号化形式で符号化したテキストの先頭につける数バイトのデータのことである。このデータを元にUnicodeで符号化されていることおよび符号化の種類の判別に使用する。
引用: [Wikipedia] より
上記では、「UTF-8」という文字コードを示すことになるようですね。
「UTF-8のファイルですよ」ということを示すためにファイルの先頭にセットする場合には、
これを8ビット符号なし整数値を表現可能なUint8Arrayでバイナリデータとしてセットするようです。
Blob
Blobとは、Binary Large OBjectの略であり、バイナリデータを表すオブジェクトです。
Web上でファイルのやり取りをするFileAPIでもこのBlobが利用されており、
ファイルを出力する際にもこのBlobを利用してファイルを生成します。
Blobでファイルを生成するには、第一引数に配列でセットしたいデータを
第二引数にMIME タイプを指定する形でファイルを生成します。
let blob = new Blob([bom, data], {type: 'text/csv'});
なお、第一引数に指定できる型は、ArrayBuffer, ArrayBufferView, Blob, DOMStringのいずれかとなっており、
DOMStringって何だろうと調べてみたところ
DOMString は、UTF-16 文字列です。JavaScript では UTF-16 文字列を用いるため、DOMString は直接 String に対応します。
引用: [DOMString | MDN web docs] より
と普段JavaScriptで利用している文字列でした。
ファイルダウンロード
URLインタフェースのcreateObjectURLメソッドを利用して、
指定された File, Blob, MediaSourceなどのオブジェクトを参照することが出来るURLの発行を行います。
今回は、そのURLをa要素のhref属性にセットしてclickイベントを発火させることで
自動でダウンロードさせるような仕組みで動作しているようです。
まとめ
昨年から個人的にJavaScriptでバイナリデータを扱うことがすごく多くなっており
今回ファイル領域までしっかり調べることが出来たのは良い機会になりました。
ファイルの作成などわざわざサーバ側で処理をしなくともブラウザ側だけで処理が完結出来るのは、
サーバの負荷対策や無駄な通信量がかからない点でもメリットがありそうですね。
余談
ちなみにこのBOMについて別案件でPHPでCSVを読み取り処理するプログラムがあったのですが、
その時に「CSVの先頭行が読み込まれない問題」でハマっていたことがありました。
このあたりの知識が無いと思わぬところでハマってしまうんだなと感じた瞬間でした。