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.1Problem
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:
- Missing
enctype="multipart/form-data"on the HTML form - Field name mismatch between the form and multer configuration
- Storage configuration not set up properly
- File size limits not configured, leading to memory issues
The multer pipeline looks like this:
Request → Parse multipart → Storage engine → File object → Route handlerSolution
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
- Always set file size limits - Default is unlimited, which is a DoS vector
- Use unique filenames - Never use the original filename to prevent overwrites
- Validate file types - Check both extension and MIME type
- Use memory storage for small files that need immediate processing
- Handle multer errors explicitly - Do not let them crash your server
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.