ファイルのやりとりなどで使うことが多いmultipart/form-dataですが、今回は、Golnagでmultipart/form-dataを使ってファイルのアップロードを作っていた際に詰まった点について、紹介します。

コンテンツの種類

フロントエンドとバックエンドのデータのやり取りでは、htmljavascriptpngjsonなど様々な種類のデータを扱います。その際にやりとりするデータの種別をMIME Typeを利用して、やり取りするデータ種別を指定します。

multipart/form-dataとは何か

複数の種類のデータを一度に扱える形式です。HTMLフォームなどよく利用されるケースが多いです。また、ファイルアップロードでよく利用されます。下記にフォームの例を示します。

<!--multipart/form-data形式でPOSTする例-->
<form method="POST" action="/upload" enctype="multipart/form-data">
    <input type="text" name="title" value="Hello"/><br>
    <input type="file" name="file"/><br>
    <input type="submit" value="SUBMIT"/>
</form>

javascriptでの例は、下記のようにします。$axiosを使っていますが、FormData()を使って、キー、バリュー形式で指定していきます。


    const uploadMovie = async () => {
      const params = new FormData();
      params.append('title', '1111')
      // inputで入力したファイルを指定する
      params.append('file', fileRef.value)
      const res = await $axios.$post('upload', params, {
        headers: {
          'content-type': 'multipart/form-data',
        },
      })
      console.log(res)
      console.log('uploadMovie')
    }

サーバサイドの実装例

下記の例は、fileとしてデータをやり取りした場合の例です。

package main
import (
	"encoding/base64"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
)

func main() {
	http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
		file, _, err := r.FormFile("file")
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		defer file.Close()
		dst, err := os.Create("data.dat")
		if err != nil {
			log.Printf("err %v", err)
		}
		defer dst.Close()
		if _, err = io.Copy(dst, file); err != nil {
			log.Printf("err %v", err)
		}
		log.Printf("complite")
	})
	http.ListenAndServe(":8080", nil)
}

dataURL形式のデータをやり取りする場合

最近では、HTMLにdataURLとしてBase64エンコードしたデータが直接書かれているケースがあります。そのようなケースでは、ファイルに保存せずにデータを直接おくりたいケースもあると思います。そのようなケースでは、下記のようにします。

package main
import (
	"encoding/base64"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
)
const (
	defaultMaxMemory = 3200 << 20 // 3200 MB
)
func main() {
	http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
		r.ParseMultipartForm(defaultMaxMemory)
		data := r.FormValue("mp4")
		i := strings.Index(data, ",")
		dec := base64.NewDecoder(base64.StdEncoding, strings.NewReader(data[i+1:]))
		dst, err := os.Create("data.dat")
		if err != nil {
			log.Printf("err %v", err)
		}
		defer dst.Close()
		if _, err = io.Copy(dst, dec); err != nil {
			log.Printf("err %v", err)
		}
		log.Printf("complite")
	})
	http.ListenAndServe(":8080", nil)
}

ParseMultipartFormで3200MBのデータでも扱えるようにしています。デフォルトでは、32MBが上限となっています。32MB以上のデータやり取りがある場合には、予めサイズを大きくする必要があります。

今回は、ファイルのやり取りなどがある開発でよく利用するmultipart/form-dataについて紹介しました。