Storing Uploaded Files and Serving Them in Express.js

When building modern web applications, handling file uploads (images, PDFs, videos, etc.) is a common requirement.
But uploading is just one part β we also need to store files properly, serve them efficiently, and secure them.
In this blog, weβll learn everything end-to-end β from storage strategies to serving files via URLs in Express.js.
π How File Upload Works?
Before diving deep, letβs understand the flow:
Client (Browser)
β
Upload Request (multipart/form-data)
β
Express Server (Multer or similar middleware)
β
File Stored (Local / Cloud)
β
File Served via URL
Where Uploaded Files Are Stored
There are two main storage approaches:
β Option 1:
Local Storage (Server File System)
Files are stored inside your project folder.
Example structure:
project/
β
βββ uploads/
β βββ images/
β βββ documents/
β
βββ server.js
Example using Multer:
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname);
}
});
const upload = multer({ storage: storage });
π Files will be stored inside /uploads.
βοΈ Option 2:
External Storage (Cloud)
Instead of storing files on your server, you use services like:
AWS S3
Cloudinary
Firebase Storage
π Best for:
Scalability
Production apps
Handling large files
Local Storage vs External Storage
| Feature | Local Storage | External Storage |
|---|---|---|
| Setup | Easy | Moderate |
| Cost | Free | Paid |
| Scalability | Limited | High |
| Performance | Depends on server | Optimized CDN |
| Best For | Small apps / learning | Production apps |
π Conclusion:
Use local storage for development
Use cloud storage for production
π Folder-Based Storage Structure
Organizing files properly is very important.
π Good Practice Structure:
uploads/
β
βββ images/
β βββ profile/
β βββ posts/
β
βββ documents/
β βββ pdfs/
β βββ resumes/
π Benefits:
Easy to manage
Cleaner structure
Faster debugging
π Serving Static Files in Express
Now comes the important part β how users access uploaded files.
Express Static Middleware:
const express = require('express');
const app = express();
app.use('/uploads', express.static('uploads'));
This means:
File stored at: uploads/image.png
Accessible via URL: http://localhost:3000/uploads/image.png
πAccessing Uploaded Files via URL
Once files are stored and static middleware is set:
Example:
app.post('/upload', upload.single('file'), (req, res) => {
res.json({
fileUrl: `http://localhost:3000/uploads/${req.file.filename}`
});
});
π Now frontend can directly use:
<img src="http://localhost:3000/uploads/12345-image.png" />
π Static File Serving Flow
Browser Request
β
GET /uploads/image.png
β
Express Static Middleware
β
Search in "uploads/" folder
β
File Found β Sent to Browser
β
File Displayed
π Security Considerations for File Uploads
This is VERY IMPORTANT (often ignored by beginners).
β Risks:
Malicious files (e.g., .exe, scripts)
Large file attacks (DoS)
Path traversal attacks
β Best Practices:
- Validate File Type
fileFilter: (req, file, cb) => {
if (file.mimetype.startsWith('image/')) {
cb(null, true);
} else {
cb(new Error('Only images allowed'), false);
}
}
- Limit File Size
limits: { fileSize: 2 * 1024 * 1024 } // 2MB
- Rename Files Safely π Avoid original names (can be risky)
π Avoid original names (can be risky)
Date.now() + '-' + file.originalname
- Store Outside Public Root (Advanced)
π Serve via controlled routes instead of direct access
5. Use Authentication
π Only logged-in users should upload/download files
Now Putting It All Together (Complete Example) JavaScript
const express = require('express');
const multer = require('multer');
const app = express();
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, 'uploads/'),
filename: (req, file, cb) =>
cb(null, Date.now() + '-' + file.originalname)
});
const upload = multer({ storage });
app.use('/uploads', express.static('uploads'));
app.post('/upload', upload.single('file'), (req, res) => {
res.json({
message: 'File uploaded successfully',
url: `http://localhost:3000/uploads/${req.file.filename}`
});
});
app.listen(3000, () => console.log('Server running on port 3000'));
Conclusion
Uploading and serving files in Express.js is a simple but important backend skill. By storing files properly, serving them with URLs, and following basic security practices, you can build reliable features like image or document uploads. Understanding this flow helps you create real-world, production-ready applications.




