結構探してベストプラクティスっぽいものが無かったので記録として。
サーバーサイドでデータを作る
CSV 形式でデータをダウンロードさせる方法はいくつかあるのですが、Javascript のコードが UTF8 になっているので、これ以外の形式でダウンロードさせるときには結構工夫が必要です。
特に、古めの Excel で直接開こうとするとき SJIS コードが必要になり、これが結構厄介です。
方法としては、
- サーバーサイドの Laravel 側で SJIS 形式でデータを作って、ブラウザで直接ダウンロード
- クライアントサイドの Javascript で SJIS 形式データを作って、ダウロードさせる
前者のほうが簡単に見えますが、ブラウザ側で Vue.js を使ったりして、WEB API 経由でデータを取ろうとする途端に厄介毎に巻き込まれます。
ならば、後者のほうで UTF8 で WEB API を呼び出して、Vue.js 側で SJIS 変換すればよい、と思いつつもピッタリのサイトが見つかりませんでした。
結論から言えば、encoding.js を使って UTF8 から SJIS に変換できます。
Vue.js でダウンロードさせる
手順は2つになります。
- Web API 経由で、JSON 形式でデータを取得
- 取得したデータを encoding.js でコンバートしてダウンロード
Web API 経由でデータを取得したほうが、Vue.js などの SPA では必須になります。API の呼び出しは axios を使っています。
axios.post(url, { params: params })
.then(function(response){
var head = "...";
var filename = "sample.csv";
this.downloadCsv(head, response.data, filename );
}
適当なパラメーターをサーバーサイドへ POST します。
取得できるデータは JSON 形式としておきます。
CDN で encoding.js をインクルードして、downloadCsv 関数と downloadCsvBlob 関数を定義します。
downloadCsvBlob 関数は、Edge と Chrome の両方を動作させるための共通関数です。この関数は、【JavaScript】Axiosを使ってCSVをダウンロードしたい | ゆとって生きたい。 から持ってきています。
<script src="https://cdnjs.cloudflare.com/ajax/libs/encoding-japanese/1.0.30/encoding.js"></script>
function downloadCsv( head, items, filename ) {
var csv = "";
items.forEach(function(it) {
csv += Object.keys(it).map(function(item){
return it[item]
}).join(',') + "\n";
}.bind(csv))
csv = head + "\n" + csv ;
csv = new Encoding.stringToCode(csv)
csv = Encoding.convert( csv, 'SJIS');
csv = new Uint8Array( csv );
this.downloadCsvBlob( csv, filename );
}
function downloadCsvBlob(blob, fileName) {
if (window.navigator.msSaveOrOpenBlob) {
// for IE,Edge
var blob = new Blob([blob], { "type" : "text/csv" });
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
// for chrome, firefox
const url = URL.createObjectURL(new Blob([blob], {type: 'text/csv'}));
const linkEl = document.createElement('a');
linkEl.href = url;
linkEl.setAttribute('download', fileName);
document.body.appendChild(linkEl);
linkEl.click();
URL.revokeObjectURL(url);
linkEl.parentNode.removeChild(linkEl);
}
}
コード変換の肝は、以下の部分ですね。
いくつか試してみたのですが、この順番でないとうまく動かないようです。変数 csv を省略しようとする動かなくなったりするのは、内部データの使い方の問題だと思いますが、ひとまず、以下の形で UTF8 から SJIS に変換ができるようになります。
csv = new Encoding.stringToCode(csv)
csv = Encoding.convert( csv, 'SJIS');
csv = new Uint8Array( csv );
これでブラウザ上のボタンをクリックすると SJIS 形式のデータをダウンロードさせることができます。当然のことながら、Web API を叩かないで、ブラウザ上だけで計算してデータをダウンロードさせることも可能です。