Node.js File Upload — Complete Guide to Handling File Uploads
In this tutorial, you will learn about Node.js File Upload. We cover key concepts, practical examples, and best practices to help you master this topic.
Node.js file upload handling involves receiving multipart form data, validating file types and sizes, storing files locally or in the cloud, and processing them for various use cases.
What You'll Learn
By the end of this tutorial, you'll use multer for file uploads, validate file types and sizes, store files locally and in cloud storage, Process images with sharp, and secure upload endpoints.
Why File Upload Matters
Most web applications need file uploads: profile pictures, document attachments, image galleries, and data imports. Secure and efficient file handling is essential for user-facing features.
Real-World Use
A document management system lets users upload PDFs, images, and spreadsheets. multer validates file types, limits sizes to 10MB, and stores files in AWS S3 with unique filenames.
File Upload Learning Path
flowchart LR
A[Authorization] --> B[File Upload]
B --> C[Caching]
C --> D[Docker]
D --> E[Deployment]
A --> F{You Are Here}
style F fill:#f90,color:#fff
Multer Setup
npm install multer
import multer from "multer";
const upload = multer({ dest: "uploads/" });
app.post("/upload", upload.single("file"), (req, res) => {
res.json({
message: "File uploaded",
file: req.file // { fieldname, originalname, encoding, mimetype, size, path }
});
});
Storage Configuration
import multer from "multer";
import path from "node:path";
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, "uploads/"),
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1E9);
cb(null, file.fieldname + "-" + uniqueSuffix + path.extname(file.originalname));
}
});
const upload = multer({ storage });
File Validation
const ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp", "application/pdf"];
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
const upload = multer({
storage,
limits: { fileSize: MAX_SIZE },
fileFilter: (req, file, cb) => {
if (!ALLOWED_TYPES.includes(file.mimetype)) {
cb(new Error("Invalid file type"), false);
return;
}
cb(null, true);
}
});
app.post("/upload", (req, res) => {
upload.single("file")(req, res, (err) => {
if (err instanceof multer.MulterError) return res.status(400).json({ error: err.message });
if (err) return res.status(400).json({ error: err.message });
res.json({ message: "Uploaded", file: req.file });
});
});
Multiple File Uploads
app.post("/upload-multiple", upload.array("files", 5), (req, res) => {
res.json({ files: req.files.map(f => ({ name: f.originalname, size: f.size })) });
});
Image Processing with Sharp
npm install sharp
import sharp from "sharp";
app.post("/upload-image", upload.single("image"), async (req, res) => {
const inputPath = req.file.path;
const outputPath = `uploads/resized-${req.file.filename}`;
await sharp(inputPath)
.resize(800, 600, { fit: "inside", withoutEnlargement: true })
.webp({ quality: 80 })
.toFile(outputPath);
res.json({ original: req.file.filename, thumbnail: outputPath });
});
Cloud Storage with S3
npm install @aws-sdk/client-s3 multer-s3
import { S3Client } from "@aws-sdk/client-s3";
import multerS3 from "multer-s3";
const s3 = new S3Client({ region: process.env.AWS_REGION });
const upload = multer({
storage: multerS3({
s3,
bucket: "my-uploads",
acl: "public-read",
metadata: (req, file, cb) => cb(null, { fieldName: file.fieldname }),
key: (req, file, cb) => cb(null, `${Date.now()}-${file.originalname}`)
}),
limits: { fileSize: 10 * 1024 * 1024 }
});
Common Mistakes
1. Not Validating File Types on Server
Client-side validation is easily bypassed. Always validate mimetype and extension on the server.
2. Using Original Filenames
Original filenames may contain path traversal sequences or special characters. Generate unique filenames on the server.
3. No File Size Limit
Without size limits, users can upload massive files that fill disk space and crash the server. Always set limits.
4. Storing Uploads in the Public Directory
Uploads in the static directory are publicly accessible by URL. Store them outside the web root or use signed URLs.
5. Not Handling Upload Errors Gracefully
Failed uploads (disk full, connection lost) must return clear error messages and clean up partial files.
Practice Questions
1. What is multer?
Multer is an Express middleware for handling multipart/form-data, primarily used for file uploads. It processes incoming files and makes them available as req.file or req.files.
2. How do you restrict file types in multer?
Pass a fileFilter function to multer configuration. Check file.mimetype against an allowed list and call cb(null, true) or cb(new Error(...), false).
3. How do you limit file size in multer?
Set the limits.fileSize option in multer configuration. Size is in bytes. Exceeding the limit throws a MulterError.
4. Why generate unique filenames for uploads?
To prevent overwriting existing files, avoid filename collisions, and prevent path traversal attacks from malicious filenames.
5. Challenge: Create an upload endpoint that accepts images, resizes them, and stores both original and thumbnail.
const upload = multer({ dest: "uploads/" });
app.post("/upload", upload.single("image"), async (req, res) => {
const thumbPath = `uploads/thumb-${req.file.filename}`;
await sharp(req.file.path).resize(200, 200).toFile(thumbPath);
res.json({ original: req.file.path, thumbnail: thumbPath });
});
FAQ
Mini Project: Avatar Upload
Build a profile avatar upload endpoint with image validation and resizing.
import multer from "multer";
import sharp from "sharp";
import path from "node:path";
const storage = multer.memoryStorage();
const upload = multer({
storage,
limits: { fileSize: 2 * 1024 * 1024 },
fileFilter: (req, file, cb) => {
if (!file.mimetype.startsWith("image/")) return cb(new Error("Images only"), false);
cb(null, true);
}
});
app.post("/api/avatar", authenticate, upload.single("avatar"), async (req, res) => {
const buffer = await sharp(req.file.buffer).resize(150, 150).png().toBuffer();
const filename = `avatar-${req.user.id}.png`;
await fsPromises.writeFile(`uploads/${filename}`, buffer);
await db.users.update(req.user.id, { avatar: filename });
res.json({ avatar: filename });
});
What's Next
Node.js Caching Redis Node.js Docker Node.js Deployment
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro