Node.js(Express)を使ってJEPG画像をアップロードして表示するというシンプルなWebアプリケーションを作ってHerokuにデプロイしたい話 on Windows

#背景
Node.jsという最近キテそうな言語を使いこなして、高級寿司を食えるかっこいいエンジニアになりたいので勉強がてらシンプルなWebアプリケーションを作った。あと、WebアプリケーションらしくHerokuにもデプロイしたいと思った。

JEPG画像のアップロードなんて余裕でしょって思っていたらめちゃくちゃ躓いたので、すべての記憶を失っているであろう未来の僕に対して記録を残す。ごちゃごちゃ書くので見づらいと思います。ごめんなさい。

#環境
OS:Windows 10 Pro (64bit)
プロセッサ: Core i5 3.20 GHz
メモリ: 24.0 GB

```
$ node -v
v12.16.2
```
```
$ npm -v
4.0.5
```
```
$ express --version
4.16.1
```

#作り方
## 1. express-generatorを使う
「巨人の肩に乗っていけぇ～」ということで
こちらの通りにやると大体できました。
[[Node.js] express + Multer を使用してファイルアップロード API を作成する](
https://dev.classmethod.jp/articles/node-express-multer-file-upload/)

ほんと神、ありがとうございます....

はじめます。
まずは、express-generator をインストールします。

```
$ npm install express-generator -g
```
express-generatorというのは、Node.jsで開発するときのフォルダ構成とかjsファイルとかのテンプレート(スケルトンともいうらしい)を作ってくれるもののようです。

任意のディレクトリで下記を実行することで実際のフォルダやファイルを作ってくれます。

```
$ express -e testproject
```

ここでは"testproject"という名前でフォルダを作成し、オプションの「-e」でEJSのテンプレートエンジンを指定します。
テンプレートエンジンにはEJS以外にもJadeとかいろいろあるみたいですが、それぞれHTML形式で記述できるそうです。HTMLぽくかけてJavascriptのコードを入れたり、変数の受け渡しとかも簡単に書けちゃうやつみたいな認識です。てか、ここではHTMLファイルの変わりとして使います。詳しくやるとGlupとかいうのも出てくるみたいです。

```
$cd testproject
$ls
> bin/ public/ routes/ views/ app.js package.json
```
上記のようなフォルダができているはずです。

また、npm install を実行し、アプリケーション実行に必要な各種パッケージをインストールします。

```
$npm install
$ls
> bin/ node_modules/ public/ routes/ views/ app.js package.json
```
おなじみのnode_modulesフォルダができたことを確認します。

この時点で

```
$ npm start
```
をしてブラウザからhttp://localhost:3000/
にアクセスすれば下記のような表示が出るはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445408/85a66811-6f6f-ea4c-1f2d-fe1d5a7e6352.png)

ちなみにここで表示されているのは
views/index.ejsのHTMLとroutes/index.jsで"title"という変数で渡した"Express"という文字列です。
なので、routes/index.jsで"Express"という文字列を"ぱんぱかぱーん"とかにすれば、ブラウザでも"ぱんぱかぱーん"と表示されるはずです。

```routes/index.js
...
/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' }); //←ここに指定したindex.ejsと変数titleに指定した文字列がブラウザに返され表示
});
...
```

```views/index.ejs
...
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
  </body>
...
```

ちょっとしたWebサイトならこれでできるね。ぱんぱかぱーん。

## 2. Multerを使う
続いて、アップロードされた画像を受け取る際に使うMulterというモジュールをインストールします。
(正確に言うとmultipart/form-data というデータ形式を扱うためのようですが、詳しいことは知りません)

```
$ npm install --save multer
```

package.json の dependencies の中に multer が追加されていることを確認。

```
$cat package.json
>{
  "name": "testproject",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "ejs": "~2.6.1",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "multer": "^1.4.2"
  }
}
```

インストールしたMulterモジュールを使用して、画像保存の記述をroutes/index.jsに追加していきます。
ここは[参考記事](https://dev.classmethod.jp/articles/node-express-multer-file-upload/)から少し変更。

```routes/index.js
xpress = require('express');
var router = express.Router();

//*** 追加1 ここから***//
var multer = require('multer');
var storage = multer.diskStorage({
  //ファイルの保存先を指定(ここでは保存先は./public/images) 
  //Express4の仕様かなんかで画像staticなファイルを保存するときはpublic/以下のフォルダに置かないとダメらしい
  //詳しくは express.static public でググろう！
  destination: function(req, file, cb){
    cb(null, './public/images/')
  },
  //ファイル名を指定
  //ここでは image.jpg という名前で保存
  filename: function(req, file, cb){
    cb(null, 'image.jpg')
  }
})

var upload = multer({storage: storage})
//*** 追加1 ここまで ***//

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

//*** 追加2 ここから ***//
//ルート (/) に対する POST リクエスト
//name タグにfileを指定したもののみ受け付ける
router.post('/',upload.single('file'),function(req,res){
  res.json({ 'result': 'success!' });
});
//*** 追加2　ここまで ***//

module.exports = router;

```

参考記事からの変更点
- 保存先を'/'(ルート)から'./public/images/'に変更。
- 保存名をfile.originalnameからimage.jpgに変更。

一応、ブラウザからもう一度確認しましょう。

```
$ npm start
```

ちょろめからhttp://localhost:3000/ にアクセス！

## 3. 画像をPOSTする（アップロードする）
POSTするのはmultipart/form-data というデータ形式なら何でもヨシッ！

「POST？multipart/form-data？何それおいしいの？」状態で、僕にはよく分からなかったので、そういう人向けにツールを使う方法とindex.ejsを書き換えて送信する方法を書きます。

### 3.1. Fiddlerを使う（とりあえずテストするには、簡単なほう）
[Fiddler](https://www.telerik.com/fiddler)というツールでPOSTだったりGETを送ることができます。
(本来はパケットキャプチャとかするのかな？)

とりあえず、インストールして起動したら、次の順で変更してきます。

0. npm startでサーバーを立ち上げる
1. Composerタブを開く
2. リクエストを"POST"に変更
3. 送信先を"localhost:3000"に書き換え
4. RequestBody枠の右上にある青字"Upload file..."から送信する画像を選択する
5. RequestBodyのnameを"file"に変更する
　(routes/index.jsでname="file"を受け付けるように指定しているため)
6. ExecuteでPOSTリクエストをサーバーに送る。
7. localhost:3000 にアクセスして{ 'result': 'success!' }が表示されているのを確認する。
8. public\images 配下にimage.jpg が保存されていることを確認する。
9. image.jpgを開いて無事に送った画像が表示されていればOK！

赤枠部分を変更
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445408/9aaa1b13-f032-1693-9da1-04d68a3345b2.png)


### 3.2. 送信用にindex.ejsを書き換える(実際にWebアプリにするなら必要なほう)

views/index.ejsを下記のように書き換えます。
特に注意なのはname=fileになっていることだけ注意してください。
fileになっていないとサーバー側で受け取れません。
(route/index.jsでname=fileを受け付けるように記述しているため)

```views/index.ejs
<!DOCTYPE html>
<html>

<head>
  <title><%= title %></title>
  <link rel='stylesheet' href='/stylesheets/style.css' />
</head>

<body>
  <h1><%= title %></h1>
  <p>Welcome to <%= title %></p>
  <form action="/" method="post" enctype="multipart/form-data">
    画像ファイル選択<br>
    <input type="file" name="file">
    <br>
    <input type="submit" name="botan" value="送信">
  </form>
</body>

</html>

```

サーバー起動してテスト

```
$ npm start
```
1. ちょろめからhttp://localhost:3000/ にアクセス！
2. 画像ファイルを選択して、「送信」ボタンを押す。
3. public\images 配下にimage.jpg が保存されていることを確認する。
4. image.jpgを開いて無事に送った画像が表示されていればOK！

## 4. アップロード後に表示させる
最後にアップロードした画像を表示するようにします。
具体的にはviewsの配下にimage.ejsという表示用のejsファイルを新規で作成し、それをレスポンスするようにします。

といってもimage.ejsに書くのはこれだけです。
参照する画像名はimage.jpgと固定にしていたのでそれを参照して表示しています。

```views/image.ejs

<!DOCTYPE html>
<html>

<body>
    <h1>画像アップロードしました</h1>
    <img src="/images/image.jpg" />
</body>

</html>

```

あれ、"/images/image.jpg"って書いてあるけど、publicディレクトリ経由しなくてもいいの？って最初思いましたが、
app.jsに下記ように記述されており、これでpublicを無視できるらしいです。静的ファイルのホスティングとか言うようです。

```app.js
....
app.use(express.static(path.join(__dirname, 'public')))
...
```


また、routes/index.jsを少し修正します。

```routes/index.js
xpress = require('express');
var router = express.Router();

//*** 追加1 ここから***//
var multer = require('multer');
var storage = multer.diskStorage({
  //ファイルの保存先を指定(ここでは保存先は./public/images) 
  //Express4の仕様かなんかで画像staticなファイルを保存するときはpublic/以下のフォルダに置かないとダメらしい
  //詳しくは express.static public でググろう！
  destination: function(req, file, cb){
    cb(null, './public/images/')
  },
  //ファイル名を指定
  //ここでは image.jpg という名前で保存
  filename: function(req, file, cb){
    cb(null, 'image.jpg')
  }
})

var upload = multer({storage: storage})
//*** 追加1 ここまで ***//

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

//*** 追加2 ここから ***//
//ルート (/) に対する POST リクエスト
//name タグにfileを指定したもののみ受け付ける
router.post('/',upload.single('file'),function(req,res){
  //image.ejsを返す
  res.render('image'); //***←　ここを修正 ***//
});
//*** 追加2　ここまで ***//

module.exports = router;

```

res.json({ 'result': 'success!' });
を
res.render('image');
に変更しています。
先ほど作成したimage.ejsを返すだけです。



## 5. Herokuにアップロードする
Herokuにアップロードするときはルートディレクトリ(testproject配下)にProcfileというファイルを作って中に

```Procfile
web: node ./bin/www
```

を記述して、git push heroku masterすればいいはず。

具体的なHerokuの登録とかアップロード方法はぐぐってくだされば・・・
僕は[この記事](https://qiita.com/shti_f/items/b4b5d830672d908eff4e)を参考にしました

※デプロイ時に下記のようなエラーがでたが[こちら](https://qiita.com/sayama0402/items/e2c9e65786259dc55e11)を参照して解決した。

```
$ git push heroku master

fatal: 'heroku' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
```


以上、間違っていることとか定石から外れていることも多いかもしれないですが、動いたのでいいかという気持ちです。
なにかあれば気軽にコメントください。

疲れたので寝ます。。。

#参考記事
- [[Node.js] express + Multer を使用してファイルアップロード API を作成する](
https://dev.classmethod.jp/articles/node-express-multer-file-upload/)
- [express-generatorを利用する[express][node.js] - 思考の葉](https://noumenon-th.net/programming/2018/12/22/express-generator01/)
- [テンプレートエンジンejsの使い方まとめ | YE-TECH](https://uetani33.net/ejs-howto/)
- [[フロントエンド] multipart/form-dataを理解してみよう - YoheiM .NET](https://www.yoheim.net/blog.php?q=20171201)
- [Node.jsでのHerokuチュートリアル前編](https://qiita.com/shti_f/items/b4b5d830672d908eff4e)
- [Express で静的ファイル（static ファイル）をホスティングする | まく ...](https://maku77.github.io/nodejs/express/static-file.html)
- [Express での静的ファイルの提供](https://expressjs.com/ja/starter/static-files.html)