Node.js (Express4)におけるmulterを使ったファイルのアップロード

Node.jsとExpress4の組み合わせでブラウザからサーバにファイルをアップロードする方法をWebで調べたところ、古い情報ばかり出てきて解決までに半日ほどかかりました。この記事では、Express4とmulterモジュールを使ったファイルのアップロード方法についてまとめてみます。
[multerモジュールのREADME](https://github.com/expressjs/multer/blob/master/README.md)を参考にさせていただきました。

#前提条件
Expressのバージョンは4.13.4、multerモジュールのバージョンは1.2.0です。

```shell-session
$ express --version
4.13.4
```

```shell-session
$ npm list multer
xxxx@y.y.y zzzzz
└── multer@1.2.0
```

# ファイルをアップロードするアプリケーションの作成
express-generatorを使って、アプリケーションの雛形を生成します。

```shell-session
$ express file-upload

   create : file-upload
   create : file-upload/package.json
   create : file-upload/app.js
   create : file-upload/public
   create : file-upload/public/javascripts
   create : file-upload/public/images
   create : file-upload/public/stylesheets
   create : file-upload/public/stylesheets/style.css
   create : file-upload/routes
   create : file-upload/routes/index.js
   create : file-upload/routes/users.js
   create : file-upload/views
   create : file-upload/views/index.jade
   create : file-upload/views/layout.jade
   create : file-upload/views/error.jade
   create : file-upload/bin
   create : file-upload/bin/www

   install dependencies:
     $ cd file-upload && npm install

   run the app:
     $ DEBUG=file-upload:* npm start
````

続いて、```package.json```を編集してmulterモジュールを追加します。

```json
{
  "name": "file-upload",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.15.1",
    "cookie-parser": "~1.4.3",
    "debug": "~2.2.0",
    "express": "~4.13.4",
    "jade": "~1.11.0",
    "morgan": "~1.7.0",
    "serve-favicon": "~2.3.0",
    "multer": "~1.2.0"
  }
}
```

```views/index.jade```を編集して、ファイルを選択してサーバにアップロードする画面を作成します。

```jade
extends layout

block content
  h1= title
  form(method="post", enctype="multipart/form-data", action="/upload")
    input(type="file", name="thumbnail")
    input(type="submit")
```

クライアントが発行するPOSTリクエストに応じてファイルをアップロードする処理を、```routes/index.js```に加えます。

```js
var express = require('express');
var router = express.Router();
var multer = require('multer');

var upload = multer({ dest: './uploads/' }).single('thumbnail');

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

router.post('/upload', function(req, res) {
  upload(req, res, function(err) {
    if(err) {
      res.send("Failed to write " + req.file.destination + " with " + err);
    } else {
      res.send("uploaded " + req.file.originalname + " as " + req.file.filename + " Size: " + req.file.size);
    }
  });
});

module.exports = router;
```

```multer({ dest: './uploads/' })```で、ファイルのアップロード先のディレクトリを指定します。ファイルを一個だけアップロードする```.single()```メソッドを用いる場合、アップロードされたファイルのオブジェクトは```req.file```に格納されます。アップロードしたファイルの名前には、multerモジュールによりランダムな値が設定されます。アップロード前のファイル名は```req.file.orignalname```、アップロード後のファイル名は```req.file.filename```から取得できます。その他、アップロード先のディレクトリを```req.file.destination```から、アップロードしたファイルのサイズを```req.file.size```から取得することができます。

# ファイルのアップロード
では、さっそくアプリケーションを起動してみましょう。

```shell-session
$ npm install
npm WARN deprecated jade@1.11.0: Jade has been renamed to pug, please install the latest version of pug instead of jade
npm WARN deprecated transformers@2.1.0: Deprecated, use jstransformer
cookie-parser@1.4.3 node_modules\cookie-parser
├── cookie-signature@1.0.6
└── cookie@0.3.1
...
$ npm start

> file-upload@0.0.0 start C:\Users\IBM_ADMIN\Documents\ITLMC\2016\blog\file-upload
> node ./bin/www
```

ブラウザで[http://localhost:3000](http://localhost:3000)にアクセスします。
![interface.png](https://qiita-image-store.s3.amazonaws.com/0/16430/eef7cc75-1982-26ec-d12d-70c4ad81f9c6.png)

「参照」ボタンからファイルを選んで、送信ボタンを押すと。。。

![upload-done.png](https://qiita-image-store.s3.amazonaws.com/0/16430/73aef153-bfed-0183-31ed-d8bbd20eef6c.png)

アップロードの完了時に```upload()```メソッドに渡したコールバック関数が呼ばれて、サーバからレスポンスが返ってきました。```IMG_4533.JPG```という名前のファイルが```2e491eefb9ec7c5858af2f0aba0ecee6```という名前でアップロードされたようです。では、アップロード先として指定した```./uploads```ディレクトリの中を見てみましょう。

```
$ ls -l ./uploads/
合計 188
-rwx------+ 1 smatsui None 188729 9月  28 15:01 2e491eefb9ec7c5858af2f0aba0ecee6
```

ファイルをアップロードすることができました！