4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DBから取得したものをcsv化。ブラウザでダウンロードできるようにする。(Microsoft Excelでも見れるようにする)。

Last updated at Posted at 2019-09-17

環境

サーバーサイド

  • フレームワーク: 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を噛ませたりするのは、どうなのか?みたいな気持ちはあるのですが、この辺もし良い方法が他にあったら、教えてください。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?