【Node.js】Expressとmulterを使って画像アップロードを実装する（multer, formData, axios）

#はじめに
メインはフロントですが、趣味でExpressを触っている者です。フロント側で画像アップロードを行う際はFileAPIを使ってDataUrlを作ってからサーバーに送る。という流れになると思うのですが、Expressで実装するとなるといろいろ前提が違ってハマったのでメモします。

#実装したコード

```javascript
// 画像アップロード

app.post(
  '/photo/upload',
  // 解説①↓ ==========
  upload.any(), 
  // 解説①↑ ==========
  async (req: any, res: express.Response) => {
    // 解説②↓ ==========
    const customeId = 1
    const formData = new FormData()
    const file = req.files[0]
    formData.append('files[]', file.buffer, file.originalname)
    formData.append('customer_id', customeId)
    // 解説②↑ ==========
    try {
      const result = await axios({
        url: `< ここにAPIのURLを載せる >`,
        method: 'POST',
        // 解説③↓ ==========
        headers: {
          ...formData.getHeaders(),
          Authorization: req.headers.authorization,
          Accept: 'application/json',
        },
        // 解説③↑ ==========
        data: formData,
      })
      res.json({ ...result.data })
    } catch (error: any) {
      console.log(error.message, 'エラーが発生しています。')
    }
  }
)
```
## 解説①（multer）
```javascript
upload.any(),
```
クライアント側から送られてきた画像情報を受け取るためにmulterを使います。multerを使わないと`req.data`の中に画像情報がないよー。という状況に陥ってしまいます。以下公式から引用。

>Multer adds a body object and a file or files object to the request object. The body object contains the values of the text fields of the form, the file or files object contains the files uploaded via the form.

今回はFFB採用を想定してここからさらにバックエンドサーバーに送る想定なのでクライアントサーバー内にファイルを格納する必要はありません。そのため今回は`upload.any()`を使っています。

## 解説②（formData）
```javascript
formData.append('files[]', file.buffer, file.originalname)
```
そもそもformタグに関しての認識が全然できていませんでした。例えばvue.jsを使ったらformタグを使わなくても入力内容をサーバーへ送れてしまうので、恥ずかしながらformタグの存在は知っていましたが、実際に使ったことがなかったため時間がかかってしまいました。反省です。formDataはformタグ内のinputでやっていることをサーバー側でやっているイメージです。

### ポイント1（formDataで配列を送る）
filesは配列で`[ 画像情報①,  画像情報②, 画像情報③, ]`のように送りたいため`files[]`と記述しています。

>key[]=value 形式の配列はサーバサイドでは [1,2,3] のような配列にパースされます。

とのことですが、はじめは`file.buffer`の方に配列をつけていてエラーが止まりませんでした。こんな感じです。気をつけてください。

```javascript
// これだとエラーになる
formData.append('files', [file.buffer], file.originalname)
```

### ポイント2（formDataで第3引数を入れている理由）

>form-dataは、入力クラス次第で、ファイル名などの情報を自動的に推測してくれるが、ファイルのメタ情報が失われている場合、form.appendの第三引数で明示的に指定する必要がある。

ということで通常のformDataの場合は第３引数に値を入れる必要はないのですが、引用内容ファイルを扱うときには気を付けなければいけません。

## 解説③（formData.getHeader）
```javascript
headers: {
          ...formData.getHeaders(),
          Authorization: req.headers.authorization,
          Accept: 'application/json',
        },
```
getHeaderに関しては調べてば出てくる情報だと思うのですが、念のため。`getHeders()`はformdataのメソッドでcontent-typeを提供してくれます。authrizationなどのオプションも追加する場合は1階層下に設定しなくてはいけないのでスプレッド構文でcontent-typeだけオブジェクトから解放するようにしてください。

# さいごに
multerに関してはもっと深掘りをする必要があると思いました。
また、画像のアップロードに関してはアプリケーションにとって必須の機能になるのでExpressではどのように実装するのか全体像を把握することができてよかったです。今まででログイン機能等のセキュリティ関連、そして今回の画像アップロードが理解でき簡単なCRODアプリならすぐに作れそうだなと自信がつきました。

# 参考URL

https://qiita.com/ryo511/items/1d5308e60802afccff26

https://qiita.com/itoudium/items/d9f04f379d34265387d8#axios%E3%81%A7formdata%E3%82%92%E9%80%81%E3%82%8B

https://qiita.com/kazu_death/items/a94ac4ae4d71928920c5

https://maximorlov.com/send-a-file-with-axios-in-nodejs/#file-as-a-buffer