ボタンをクリックしたらcsvや画像ファイルなど保存できる機能が実装されているのをよく見るのですが、どのようにやっているのか気になったため、実装してみました。
実装方法
HeaderにContent-Dispositionを設定するようにします。
Content-Dispositionは、コンテンツをwebページとして表示するのか、ローカルにダウンロードするものかを指定することができます。
Content-Dispositionには、2種類あり
-
inline
webコンテンツ、コンテンツの1部として表示します。 -
attachment
ローカルにダウンロードするものであることを示します。
今回はcsvとしてダウンロードしたいのでHeaderに以下のようなものを追加することで、APIにGETリクエストを送ったらcsvをダウンロードできるようにすることができます。
Content-Disposition: attachment; filename="filename.csv"
実装
今回は以下のようにGo実装してみます。
今回はフレームワークとしてGinを使用していて、ginのWriterをcsvのwriterに渡すことで、レスポンス内容にcsvを書き込んでいます。
あとは先述したHeaderを見てブラウザ等ではファイルがダウンロードされるという仕組みになっています。
func (h Handler) GetCSV(c *gin.Context) {
//csvに入れるmockデータ
data := [][]string{
{"ID", "Name", "Email", "Survey Date", "Status"},
{"1", "田中太郎", "tanaka@example.com", "2024-01-15", "完了"},
{"2", "佐藤花子", "sato@example.com", "2024-01-16", "未完了"},
{"3", "鈴木一郎", "suzuki@example.com", "2024-01-17", "完了"},
}
//file名を指定
filename := fmt.Sprintf("survey_%s.csv", time.Now().Format("20060102_150405"))
//コンテンツがcsvであることを明記
c.Header("Content-Type", "text/csv; charset=utf-8")
//ダウンロードするものであることを明記
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
writer := csv.NewWriter(c.Writer)
defer writer.Flush()
//csvに書き込み
for _, record := range data {
if err := writer.Write(record); err != nil {
custom_error.CreateErrorResponse(c, custom_error.NewInternalServerError(custom_error.InternalErrorInternal, err.Error()))
return
}
}
}