環境
サーバーサイド
- フレームワーク: goa
- ORM:gorm
- DB: MySQL
フロントエンド
- Vue.js(単一ファイルコンポーネント)
- axios(Vue内で利用)
- file-saver(Vue内で利用)
決まっている要件
- csvをダウンロードするAPIを作成。
- ブラウザ上でボタンをクリックすると、APIが叩かれ、csvがダウンロードできるようにする。
- Microsoft Excelでも文字化けせず、見れるようにする。
前置き
今回、goa
のcontrollerの一つをcsvダウンロード用にしています。
goa
でcontrollerを作るなどには言及しません。
サンプルコード
サーバーサイド(golang)
package controller
import (
"bytes"
"encoding/csv"
"strconv"
"github.com/jinzhu/gorm"
"github.com/goadesign/goa"
)
// 色々他のコントローラがあることを想定...
// ...
// GetCsv runs the get csv action.
func (c *MyAppController) GetCsv(ctx *app.GetCsvMyAppContext) error {
// MyAppController_GetCsv: start_implement
// ...DBから値を得るなどしていることを想定
// ...
b := &bytes.Buffer{}
w := csv.NewWriter(b)
headers := []string{"id", "名前", "猫種"}
w.Write(headers)
w.Flush()
catDB := models.NewCatsDB(c.db)
cats, _ := catDB.List(ctx.Context)
for _, cat := range cats {
record := []string{}
record = append(record, strconv.Itoa(cat.ID))
record = append(record, cat.Name)
record = append(record, cat.Breed)
w.Write(record)
w.Flush()
}
rw := ctx.ResponseWriter
rw.Header().Set("Content-Type", "text/csv")
rw.Header().Set("Content-Disposition", "cat_list.csv")
csvLength := strconv.Itoa(b.Len())
rw.Header().Set("Content-Length", csvLength)
rw.Write(b.Bytes())
return ctx.NoContent()
// MyAppController_GetCsv: end_implement
}
// 色々他のコントローラがあることを想定...
// ...
フロントエンド(Vue.js)
vue.js
<template lang="pug">
div
// ...
// ...
button(@click="csvDownLoad()") CSVダウンロード
</template>
<script>
import saveAs from 'file-saver'
export default {
...
...
methods: {
csvDownLoad () {
// axiosは、main.jsなどにグローバルで登録をしておき、this.$axiosで呼び出せるようにしてあります。
this.$axios
.get(`${process.env.API_URL}/api/cats/csv`) // getで呼び出すAPIは、今回はgoaで生成したものに合わせているという前提になります。
.then(response => {
let mimeType = response.headers['content-type']
const name = response.headers['content-disposition']
const data = response.data // サーバーサイド(goa)からのレスポンスに合わせている。
const bom = new Uint8Array([0xEF, 0xBB, 0xBF]) // Microsoft Excelでも見れるように
、BOMを付与。
const blob = new Blob([bom, data], {type: mimeType})
saveAs(blob, name)
})
},
...
...
}
}
</script>
個人的ポイント
axiosでサーバーサイドからのレスポンスを取得してから、BOM付与処理をする。
サーバー内にのどこかのディレクトリ(例えば/tmp
とか)に、そのまま書き込むのであれば、サーバサイドで、SJIS変換処理(BOMをつけるなど)をすれば良いと思います。
しかし、golangのResponseWriter
を噛ませて、ブラウザ側でファイル取得をすると、サーバーサイド側でSJIS化の処理していても、UTF-8フラグがついてしまうのか、文字化けしました。
なので、Vue.js側でBOMを付与して、BOM付きのutf-8にすることによって、Microsoft Excelでも文字化けせずに表示できるようにしました。
goaを使っていて、さらにResponseWriter
を噛ませたりするのは、どうなのか?みたいな気持ちはあるのですが、この辺もし良い方法が他にあったら、教えてください。