Express.js
express4

express4 でファイルアップロード

ファイルのアップロードというか写真のアップロードをしたい。

クライアント側Angular

  private readFile(file: any) {
    const reader = new FileReader();
    reader.onloadend = () => {
      const formData = new FormData();
      const imgBlob = new Blob([reader.result], {type: file.type});
      formData.append('file', imgBlob, file.name);
      this.postData(formData);
    };
    reader.readAsArrayBuffer(file);
  }

  private postData(formData: FormData) {
    this.http.post("http://192.168.230.22:8080/v1/user_photo", formData)
      .catch((e) => this.handleError(e))
      .map(response => response.text())
      .finally(() => this.loading.dismiss())
      .subscribe(ok => this.showToast(ok));
  }

サーバ側Express

req.bodyでも、req.filesでも、中身を取得できないと悩む始末
reqの内容をダンプしてみている

api |   headers:
api |    { connection: 'upgrade',
api |      'x-forwarded-for': '172.28.0.1',
api |      host: '192.168.230.22',
api |      'content-length': '17985467',
api |      'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryWxpDgOaYBsqFrsAB',
api |      origin: 'file://',
api |      accept: 'application/json, text/plain, */*',
api |      'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Mobile/14G60',

'content-type': 'multipart/form-data

Expressのapp.jsには、body-parserが入っています。
これが曲者。

app.use(bodyParser.json());

app.use(bodyParser.urlencoded({
   extended: false
 }));

body-parserのモジュールは、jsonとurlencode済みのものしか扱いません。ファイルアップロードのようにmultipartのものは扱わないのです。

なので、上記部分をコメントアウトか、パスによって分岐をするかして、ファイルアップロードの際には適用しないようにする

以下では、ContentTypeで適用

// parse application/json 
app.use(bodyParser.json({ type: 'application/*+json' }));

// parse application/x-www-form-urlencoded 
app.use(bodyParser.urlencoded({
  extended: false,
  type: 'application/x-www-form-urlencoded'
}));

multer

いくつかファイルアップロードライブラリがあるので、お好みで。
今回はmulterを利用。以下に簡単サンプルコード。

express/uploadにファイルが作られます

var multer  = require('multer');
var upload = multer({ dest: 'uploads/' })

router.post('/user_photo', upload.any(), (req, res, next) => {
    logger.info(req.files);
    res.status(200).end();
  });

log出力内容

api | 2017-08-15T12:33:57+0000 <info> api.js:269 (router.post) [ { fieldname: 'file',
api |     originalname: 'cdv_photo_015.png',
api |     encoding: '7bit',
api |     mimetype: 'null',
api |     destination: 'uploads/',
api |     filename: '1f73e8b4197b863716f6b21b447551e9',
api |     path: 'uploads/1f73e8b4197b863716f6b21b447551e9',
api |     size: 18025712 } ]