Vue.jsとExpressを使って画像アップロードをしてみる

はじめまして、PMをやっているtatsukenと申します。はじめまして
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思います
#はじめに
Vue.jsからリクエストを投げてExpressのpublicディレクトリに画像をアップロードしてみたいとおもいます。
今回画像アップロードには[multer](https://www.npmjs.com/package/multer)というライブラリを使って行きます。

## 実装
###使用するライブラリのインストール
* [axios](https://www.npmjs.com/package/axios)
    * `npm install axios -s` Vueプロジェクトの中でinstallしてください
* [multer](https://www.npmjs.com/package/multer)
    * `npm install multer -s`　Expressプロジェクトの中でinstallしてください

### Vueの実装
まず一つvueファイルを作ってください

```Uplord.vue
<template>
  <div>
    <form>
      <input type="file" id="file" v-on:change="onFileChange">
      <input type="submit" value="decide" @click="submitClick">
    </form>
  </div>
</template>
<script>
import Axios from "axios";
export default {
    data() {
        return {
            imageFile: null
        };
    },
    methods: {
        //画像が選択されたとき呼ばれる
        onFileChange(e) {
            this.imageFile = e.target.files || e.dataTransfer.files;
        },
        //submitされたときに呼ばれる
        async submitClick() {
            try {
                const formData = new FormData();
                formData.append("file", this.imageFile[0]);
                const config = {
                    headers: {
                        "content-type": "multipart/form-data",
                    }
                };
                let res = await Axios.post("/image", formData, config);
                console.log(res);
                if (res.data.status === "error") {
                    alert(res.data.error);
                } else {
                    alert("登録完了")
                }
            } catch (error) {
                alert("画像の送信に失敗しました");
            }
        }
    }
}
</script>
```

* ここではformDataを使って画像をpostしています。
* `formData.append("key",value)`を使うことでkey:valueを対応させる形で任意のデータを乗せることが出来ます。
 
###注意点
* valueにはオブジェクトなどは乗せることが出来ません。基本的にkeyとvalueを一つずつ載せてください。
* formDataに載せたvalueはStingになってしまいます。

###Expressの実装
src/index.jsに以下を書いていきましょう

```index.js
const express = require('express')
const multer = require('multer');
const app = express()
const storage = multer.diskStorage({
    // ファイルの保存先を指定(今回はsrc/public/image以下に画像を保存)
    destination: function (req, file, cb) {
        cb(null, 'src/public/image')
    },
    // ファイル名を指定(オリジナルのファイル名を指定)
    filename: function (req, file, cb) {
        //　Math.random().toString(36).slice(-9)で乱数を生成
        const imageName = `${Math.random().toString(36).slice(-9)}_${Date.now()}.png`
        cb(null, imageName)
    }
})

const upload = multer({
    storage: storage
}).single('file')

app.post('/image', (req, res) => {
    upload(req, res, (err) => {
        if (err) {
            //アップロード失敗した場合
            res.json({
                status: "error",
                error: "fail to uplord image"
            })
        } else {
            //アップロード成功した場合
            res.json({
                status: "sucess",
                // ファイル名を返す
                path: res.req.file.filename
            })
        }
    })
});
```
* `const storage`で画像の保存先をファイル名を指定
* ここではファイル名を乱数と時間を組み合わせていますがこれではユニークとは言えません。ユニークにしたい方はuserIdと時間を組み合わせるなどしてください。
* `/image`のエンドポイントでuplordを呼び出しています。ただアップロードするだけでしたらもっと簡単に出来ます。([詳しくはこちら](https://www.npmjs.com/package/multer))しかしここではアップロード出来たかどうかのエラーハンドリングを行いたいのでこのような形になっています。
* アップロードされた画像のファイル名は`res.req.file.filename`で取得する事ができます。
* 無事フロントで`res.data.status`が`sucsess`になっていれば登録完了です。

#最後に
[multer](https://www.npmjs.com/package/multer)と[axios](https://www.npmjs.com/package/axios)でお手軽にVueからExpressに画像をアップロードすることが出来ました。
機会があれば是非試してみてください
なにか間違いなどありましたら教えていただけると幸いです。