Uploading files to a Node.js server requires parsing multipart/form-data HTTP request streams, enforcing strict file size limits, validating real file types, and saving files under safe randomized names. Raw node:http does not parse multipart boundaries automatically, so production apps stream uploads using libraries like busboy, formidable, or multer.
Instead of buffering an entire 50 MB video upload in Node.js memory first, streaming parsers pipe incoming chunks straight to temporary disk storage or S3 as bytes arrive.
flowchart LR
A["Browser FormData POST"] --> B["Multipart Parser (Busboy)"]
B --> C{"Size <= Limit?"}
C -->|"No"| D["Abort Stream (413)"]
C -->|"Yes"| E["Check Magic Bytes / MIME"]
E -->|"Valid"| F["Save with UUID Filename"]
import path from 'node:path';
import crypto from 'node:crypto';
const ALLOWED_EXTENSIONS = new Map([
['image/png', '.png'],
['image/jpeg', '.jpg'],
['application/pdf', '.pdf'],
]);
export function buildSafeUploadPath(uploadDir, detectedMime) {
const ext = ALLOWED_EXTENSIONS.get(detectedMime);
if (!ext) {
throw new Error('Unsupported file type');
}
const randomName = `${crypto.randomUUID()}${ext}`;
return path.join(uploadDir, randomName);
}
console.log(buildSafeUploadPath('/var/app/uploads', 'image/png'));
Never rely on file.originalname or the client's Content-Type header alone—attackers can rename an HTML or script payload to photo.jpg. Inspect magic bytes (the first bytes of the file buffer) using a package like file-type, enforce limits: { fileSize, files }, and store uploads outside your public static folder or on object storage.
../../server.js or index.html can overwrite source code or enable stored XSS. Always generate a UUID filename.multer.memoryStorage() for large files: Concurrent 20 MB uploads in memory can crash a 512 MB container with an out-of-memory error. Stream to temp disk or S3 instead.finally block so /tmp does not fill up.req.body stay empty on file upload forms?Standard express.json() and URL-encoded parsers ignore multipart/form-data. You need a multipart parser like multer or busboy to read both file streams and text fields.
Yes. In large apps, Node.js generates a short-lived pre-signed S3 upload URL so the browser uploads heavy files straight to cloud storage without tying up Node bandwidth.
Return 413 Payload Too Large with a clear JSON error telling the user the maximum allowed size.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
Experiment with the code from this lesson in our interactive playground.