🤖

Goでmultipart.fileheader型の画像をFormから複数受け取ってDBに保存する

2022/08/16に公開

環境

  • go1.18.3
  • gorm.io/gorm v1.23.8
  • gorm.io/driver/mysql v1.3.5

実装

package model

type Image struct {
	Id int
	Source []byte `json:"source" gorm:"size:70000"`
}
package database

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"example.com/model"
)


var db *gorm.DB

func DbConn() {
	dsn := "root:hoge@tcp(127.0.0.1:3306)/todo?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	db.AutoMigrate(&model.Todo{}, &model.Image{})
}

func Create(obj interface{}) {
	db.Create(obj)
}
package imagecontroller
import(
	"fmt"
	"net/http"
	"io"
	"example.com/database"
	"example.com/model"
)

func Create(w http.ResponseWriter, r *http.Request){
	r.ParseMultipartForm(32 << 20)
	mf := r.MultipartForm
	images := mf.File["images[]"]
	
	for _, image := range images {
		fmt.Printf("%T\n", image) // *multipart.FileHeader
		data, _ := image.Open()
		source, _ := io.ReadAll(data)
		fmt.Printf("%T\n", source) // []uint8
		image := model.Image { Source: source}
		database.Create(&image)
	}
}

解説

  1. net/httpでmultipart.Formを扱うためにr.ParseMultipartFormを宣言し、
    r.MultipartForm("key名")で値を取得する。
	r.ParseMultipartForm(32 << 20)
	mf := r.MultipartForm
	images := mf.File["images[]"]

https://pkg.go.dev/net/http
// MultipartForm is the parsed multipart form, including file uploads.
// This field is only available after ParseMultipartForm is called.
// The HTTP client ignores MultipartForm and uses Body instead.
MultipartForm *multipart.Form

  1. mysqlに画像を保存するためには*multipart.FileHeaderからbinaryに変換する必要があるので、ioパッケージのReadAll関数を利用する。
    multipart.FileHeaderはio.Readerを持っていないので、
    Open関数で
    Fileに変換してからReadAll関数の引数に渡す。
	for _, image := range images {
		fmt.Printf("%T\n", image) // *multipart.FileHeader
		data, _ := image.Open()
		source, _ := io.ReadAll(data)
		fmt.Printf("%T\n", source) // []uint8
		image := model.Image { Source: source}
		database.Create(&image)
	}

まとめ

画像を直接DBに保存するとネットワークとDBに結構負荷がかかってしまうので、
パフォーマンスを考えると、S3などのオブジェクトストレージを使って、
相対パスをDBで管理するとかが良い。

Discussion