環境
- Node.js、Express
- multer
multer について
- HTTP 通信で送られた Content-Type が multipart/form-data のファイルを処理するためのミドルウェア
- ファイルをメモリやストレージに保存することができる
やりたいこと
- HTTP リクエストで受信した multipart/form-data のファイルをメモリに保存した後、ストレージに保存したかった
- 以下の方法で実現しようとした
- メモリに保存するときは、multer.memoryStorage()
- ストレージに保存するときは、multer.diskStorage()
事象
- multerの.single() を 2 つミドルウェアに定義するとエラーになる
router.post(
"/upload/new",
// メモリに保存
multerMemory.single("markdown"),
async (req, res, next) => {
req.headers.uuid = crypto.randomUUID();
req.headers.userEmail = await getUserEmail(user_id);
req.headers.filedir = `${process.env.STR_DIR_PATH}/users/${req.headers.userEmail}/files/${req.headers.uuid}`;
// ディレクトリを作成
await fs.promises.mkdir(req.headers.filedir, { recursive: true });
next();
},
// ストレージに保存
multerStorage.single("markdown"),
// 以降のミドルウェアは省略
エラー文
Error: Unexpected end of form
at Multipart.\_final (アプリのパス\node_modules\busboy\lib\types\multipart.js:588:17)
at callFinal (node:internal/streams/writable:698:12)
at prefinish (node:internal/streams/writable:710:7)
at finishMaybe (node:internal/streams/writable:720:5)
at Writable.end (node:internal/streams/writable:634:5)
at onend (node:internal/streams/readable:705:10)
at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
multipart/form-data のストリームが完全に終わる前に終了してしまうとこのエラーが出るようです。
解決
なぜストリームが途中で終わってしまうかの原因はわかりませんでしたが、メモリに保存したファイルを fs でストレージに移すように変更したことで解決しました。
multer でメモリ保存されたファイルは、req.file.buffer に格納されるので、それを使用しました。
router.post(
"/upload/new",
multerUpload.single("markdown"),
async (req, res, next) => {
try {
if (!req.file) {
return res.status(400).send("No file uploaded.");
}
const buffer = req.file.buffer;
const uuid = crypto.randomUUID();
const userEmail = await getUserEmail(user_id);
const filedir = `${process.env.STR_DIR_PATH}/users/${userEmail}/files/${uuid}`;
const filename = decodeURIComponent(req.file.originalname);
const filePath = path.join(filedir, filename);
// UUIDのディレクトリを作成
await fs.promises.mkdir(filedir, { recursive: true });
// ファイル保存
await fs.promises.writeFile(filePath, buffer);
Tips
ちなみに、multerの.single()はそれ自体がミドルウェア関数で、以下のようにミドルウェア関数の中に入れることはできないです。
async (req, res, next) => {
multerMemory.single("markdown")
next();
},