くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

routing-controllersで複数のファイルを別のfield名を必須じゃなくても扱えるようにする

最近はサーバサイドもTypeScriptにしていて、
routing-controllersがいい感じ。

ファイルアップロードを扱う際は、expressjs/multerを利用するけど、
若干ハマったので、そのときの備忘録。

やりたかったこと

  • 複数のファイルを同時にアップロードしたい
  • それぞれはone / twoというフィールド名
  • 各フィールドは1つで、必須ではない(0でもOK)

routing-controllersでは、こんな感じでデコレータをつけるけど、
必須ではない複数フィールドではうまくいかないっぽい。。

@Post("/files")
saveFile(@UploadedFile("fileName") file: any) {
}

問題の原因

routing-controllersのissueだとこのあたり。

multerではいくつか関数が用意されているが、それぞれ制約が異なる。
また、routing-controllersでは、singlearrayが対応している。

Multer function Files accepted n decorator
multer.none() 0 0
multer.any() 0+ n
multer.single() 1 1 @UploadedFile
multer.array() 1+ >=1 @UploadedFiles
multer.fields() 1+ >=1

なので、

  • @UploadedFile@UploadedFilesは1つしか使えない
  • 必ず指定が必要(必須になる)

という条件がある。

解決策

複数フィールドでどちらも必須の場合

どちらも必須であれば、multer.fields()を利用することができるので、
@UseBeforeを使い、@Req()から取得すればOK

import { Request } from "express";
import multer from "multer";

@Post()
@UseBefore(multer.fields([{ name: 'one', maxCount: 1 }, { name: 'two', maxCount: 1 }]))
action(@Req() req: Request) {
  const fileOne = req.files['one'][0];
  const fileTwo = req.files['two'][0];
}

複数フィールドで任意の場合

0個にも対応しているのは、multer.any()のみなので、
@UseBefore(multer.any())を利用する。

.fieldnameでフィールド名が取得できるので、
req.files.find()で対象のフィールド名のファイルを取得する形。

import { Request } from "express";
import multer from "multer";

@Post()
@UseBefore(multer.any())
action(@Req() req: Request) {
  const fileOne = req.files?.find(v => v.fieldname == "one") || null;
  const fileTwo = req.files?.find(v => v.fieldname == "two") || null;
}

以上!! routing-controllersもmulterも便利だけど、
うまくいかないと深いとこまで調べないといけないのがつらい(´・ω・`)

参考にしたサイト様