背景
backend に rails 、 front に vuejs という環境で、vuejs 側に用意してあるボタンを押すと csv ファイルがダウンロードされるという機能を作成したい。
- csv は excel で扱いたいため utf-8(bom付き) を使用する。
- csv ファイルの作成は サーバー側で行う。
- 利用ブラウザは Google Chrome を想定。
処理の流れ
- vuejs側でcsvダウンロードボタンを押す
- rails側でcsvファイルを作成する
- vuejs側にblobでファイルを受け渡しダウンロードする
つまづいたこと
rails側でcsvファイルを作成する際、以下のコードのように rails側で文字コードを utf-8(bom付き)に指定したファイルを作成した。
しかし、blob 経由の vuejs側でダウンロードするとただの utf-8 になっていた。
content.csv.ruby
require 'csv'
bom = "\uFEFF"
CSV.generate(bom) do |csv|
csv << [
'id','名前','作成日時'
]
@contents.each do |content|
csv_column_values = [
content.id,
content.name,
content.created_at
]
csv << csv_column_values
end
end
sample.vue
<template>
<div>
<button @click="downloadContent()"> CSVダウンロード </button>
<div>
</template>
・ ・ ・ 省略 ・ ・ ・
// contentコントローラーのuser_reviewアクションにpostしてcsvをblobでダウンロード
async downloadContent() {
const response = await axios.post(`/contents/user_review.csv`, {}).catch((error) => {
・ ・ ・ 中略 ・ ・ ・
});
if(response) {
const blob = new Blob([response.data], {type: 'text/csv'});
this.downloadCsvBlob(blob, "Content.csv")
}
},
// csvをダウンロードする(Chromeを想定)
downloadCsvBlob(blob, fileName) {
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);
},
結論から言うと bom付きかどうかの指定は vuejs側の blob で行わなければいけなかった。
修正内容
rails側でcsvを作成するときはutf-8を指定する。
content.csv.ruby
require 'csv'
# bom = "\uFEFF" を削除
CSV.generate(encoding: Encoding::UTF_8) do |csv| # 変更 UTF-8 とだけ指定する
csv << [
'id','名前','作成日時'
]
@contents.each do |content|
csv_column_values = [
content.id,
content.name,
content.created_at
]
csv << csv_column_values
end
end
bom付きかどうかは vuejs側の blob で行う。
sample.vue
・ ・ ・ 変更なしのため省略 ・ ・ ・
// contentコントローラーのuser_reviewアクションにpostしてcsvをblobでダウンロード
async downloadContent() {
・ ・ ・ 中略 ・ ・ ・
},
// csvをutf-8(bom付き)でダウンロードする(Chromeを想定)
downloadCsvBlob(blob, fileName) {
const bom = new Uint8Array([0xEF, 0xBB, 0xBF]); // 変更 先頭の3バイト(BOM)を変数 bom へ代入
const url = URL.createObjectURL(new Blob([bom, blob], {type: 'text/csv'})); // 変更 データの先頭に bom を配置
const linkEl = document.createElement('a');
・ ・ ・ 変更なしのため以下省略 ・ ・ ・
これでcsvファイルをbom付きでダウンロードできた。