troubleshooting2025-05-15·8·63/348

Node.js에서 파일 업로드 multer 설정 에러

Fixing common multer configuration errors for file uploads in Express.js applications including size limits and storage setup.

Node.js에서 파일 업로드 multer 설정 에러

Introduction

File uploads are a common feature in web applications, and multer is the de facto middleware for handling multipart form data in Express.js. However, misconfigured multer can lead to lost files, security vulnerabilities, and confusing error messages. Let me walk through the common issues.

Environment

node --version
# v20.11.0

npm list multer express
# my-app@1.0.0
# +-- express@4.18.2
# +-- multer@1.4.5-lts.1

Problem

A typical file upload endpoint fails with various errors:

const express = require('express');
const multer = require('multer');

const app = express();
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('file'), (req, res) => {
  console.log(req.file);
  // Sometimes undefined, sometimes missing fields
  res.json({ file: req.file });
});

Common errors seen:

Error: Multipart: Boundary not found
    at new FormData (node:internal/modules/cjs/loader:1020:11)

MulterError: Unexpected field
    at Multipart._final (node:internal/modules/cjs/loader:1020:11)

Error: ENOENT: no such file or directory, open 'uploads/undefined'

Analysis

Multer configuration issues fall into several categories:

  1. Missing enctype="multipart/form-data" on the HTML form
  2. Field name mismatch between the form and multer configuration
  3. Storage configuration not set up properly
  4. File size limits not configured, leading to memory issues

The multer pipeline looks like this:

Request → Parse multipart → Storage engine → File object → Route handler

Solution

Solution 1: Basic disk storage configuration

const multer = require('multer');
const path = require('path');
const crypto = require('crypto');

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = crypto.randomBytes(16).toString('hex');
    const ext = path.extname(file.originalname);
    cb(null, `${uniqueSuffix}${ext}`);
  }
});

const upload = multer({
  storage: storage,
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB
    files: 5
  }
});

Solution 2: File filter for allowed types

const fileFilter = (req, file, cb) => {
  const allowedTypes = [
    'image/jpeg',
    'image/png',
    'image/gif',
    'application/pdf'
  ];
  
  if (allowedTypes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error(`File type ${file.mimetype} not allowed`), false);
  }
};

const upload = multer({
  storage: storage,
  fileFilter: fileFilter,
  limits: {
    fileSize: 10 * 1024 * 1024
  }
});

Solution 3: Multiple file upload

app.post('/upload-multiple', 
  upload.array('files', 10),
  (req, res) => {
    if (!req.files || req.files.length === 0) {
      return res.status(400).json({ error: 'No files uploaded' });
    }
    
    const fileInfo = req.files.map(f => ({
      filename: f.filename,
      originalName: f.originalname,
      size: f.size,
      mimetype: f.mimetype
    }));
    
    res.json({ files: fileInfo });
  }
);

Solution 4: Memory storage for processing

const memoryUpload = multer({
  storage: multer.memoryStorage(),
  limits: {
    fileSize: 5 * 1024 * 1024 // 5MB
  }
});

app.post('/process-image', memoryUpload.single('image'), async (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'No image uploaded' });
  }
  
  // Process the buffer directly
  const buffer = req.file.buffer;
  const processed = await sharp(buffer)
    .resize(800, 600)
    .jpeg({ quality: 80 })
    .toBuffer();
  
  res.set('Content-Type', 'image/jpeg');
  res.send(processed);
});

Solution 5: Error handling middleware

// Must be after multer middleware
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    if (err.code === 'LIMIT_FILE_SIZE') {
      return res.status(413).json({ error: 'File too large' });
    }
    if (err.code === 'LIMIT_FILE_COUNT') {
      return res.status(400).json({ error: 'Too many files' });
    }
    if (err.code === 'LIMIT_UNEXPECTED_FILE') {
      return res.status(400).json({ error: 'Unexpected field name' });
    }
    return res.status(400).json({ error: err.message });
  }
  
  if (err.message && err.message.includes('File type')) {
    return res.status(415).json({ error: err.message });
  }
  
  next(err);
});

Lessons Learned

  1. Always set file size limits - Default is unlimited, which is a DoS vector
  2. Use unique filenames - Never use the original filename to prevent overwrites
  3. Validate file types - Check both extension and MIME type
  4. Use memory storage for small files that need immediate processing
  5. Handle multer errors explicitly - Do not let them crash your server

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.