ブラウザからJSでmultipart/form-dataを送信、Ginで受信する

# Reactでmultipart/form-dataを送信、Ginで受信する
めっちゃ手間取ったのでメモ書き

## multipart/form-dataとは
複数のデータを同時に送る形式
RFC2388
https://lemniscus.hatenablog.com/entry/20121027/1351329602

リクエストボディを区切り文字で区切って、複数のデータを送る
例. 
> 引用元 : https://lemniscus.hatenablog.com/entry/20121027/1351329602

```http=
-----------------------------174406726119144318502035190680
Content-Disposition: form-data; name="xxxx"

11111
22222
-----------------------------174406726119144318502035190680
Content-Disposition: form-data; name="yyyy"

on
-----------------------------174406726119144318502035190680--
```



## 送信側 : Axios (& React.js)
http requestの送信には **Axios** を使う

送る側は、 **FormData** オブジェクトを作成して、値をappendして送る
httpリクエストの構成は、

- application/json (FormData)
    - text/plane (情報のjson string)
    - image/* (画像ファイル)

の3要素となる。

### formタグ部分

```jsx


const [formData, setFormData] = useState({
    title: "",
    author: "",
    description: ""
})
const fileInput = React.createRef()

return(
    <form>
    <input type="text" name="title" value={formData.title} onChange={handleChange}/>
    <input type="text" name="author" value={formData.author} onChange={handleChange}/>
    <input type="text" name="description" value={formData.description} onChange={handleChange}/>
    <input type="file" name="image" ref={fileInput} accept="image/*"/>
    </form>
)
```
**\<input type="file"/>** のvalueは、ユーザーしか操作できないため
ファイル入力タグは **非制御コンポーネント** と呼ばれる。

Reactでは、formの各inputのvalueを state などで制御することを推奨している。
しかし、\<input type="file"/> は state で制御せず、参照するだけ。

React公式サイトにて、Reactでファイル送信フォームを作るための解説が載ってる
https://ja.reactjs.org/docs/uncontrolled-components.html#the-file-input-tag

1. コンポーネント内で、 **React.createRef()** ref を作る
2. \<input type="file" ref={}> のrefに、作成したrefを入れる
3. Submitでファイル送信するときは、 ref.current.files[0] を使う


### Submit部分

```javascript

handleSubmit = async (event) => {
        event.preventDefault()
        const submitData = new FormData()

        submitData.append("formData", JSON.stringify(formData))
        submitData.append("image", fileInput.current.files[0])

        await axios.post(`http://api/posts`, submitData,
            {
                headers: {
                'content-type': 'multipart/form-data',
            },
          })
    }
```

axiosのpostメソッドで送る。
multipart/form-data でデータを送信するには、 **FormData()** オブジェクトを作り
送信するデータ(string or binary)をFormNameにappendする

- json : JSON.Stringify()して送る
- image: バイナリとして送る

## 受信側: Golang (gin)
multipart/formdata は、バイナリや複数ファイルを送るのに優れるが
jsonフォーマットをパースするwebフレームワークの機能と相性が悪い

image はgin.Context.Request.FormFile("image")でファイル形式で取得できる
ioパッケージを使って、そのままos.Create()したファイルにコピーできる。

jsonはtext/planeで送られるので、 gin.Context.FormValue("formData")でstring値を
取得して、[]byte型にキャストしたあとjson.Unmarshal()する


```go

func(c *gin.Context) {
    // 画像の保存
    image, header, _ := c.Request.FormFile("image")
    saveFile, _ := os.Create("./images/" + header.Filename)
    defer saveFile.Close()
    io.Copy(saveFile, image)

    // json情報の取得
    jsonStr := c.Request.FormValue("formData")
    var p Post
    json.Unmarshal([]byte(jsonStr), &p)
    fmt.Println(p.Title, p.Author, p.Description)

	c.JSON(201, p)
}
```







# 参考リンク
RFC2388
https://www.ietf.org/rfc/rfc2388.txt


### React
- 【React】axiosを使用してmultipart/form-data形式の通信をする  
http://reiji1020.hatenablog.com/entry/2018/12/31/
- React公式 : 非制御コンポーネント  
https://ja.reactjs.org/docs/uncontrolled-components.html#the-file-input-tag

### Qiita
- WebAPI でファイルをアップロードする方法アレコレ  
https://qiita.com/mserizawa/items/7f1b9e5077fd3a9d336b
- Goでmultipart/form-dataをparseする  
https://qiita.com/r9y9/items/35a1cf139332a3072fc8
- \<input type="file">で同じファイルを選択してもchangeイベントをトリガーさせる  
https://qiita.com/_Keitaro_/items/57b1c5dd36b7bed08ad8