Storing Uploaded Files and Serving Them in Express

Search for a command to run...

No comments yet. Be the first to comment.
You open WhatsApp, type a message, and hit send. A single grey tick appears. You're on a train with no signal, but the message went through anyway. Or did it? That single tick is the beginning of a su

You record a Reel, add a song, trim it, and then close the app before posting. When you reopen Instagram, the draft is right there waiting. Nothing was lost. That experience feels simple. The system b

If you have built a React Native app, you have felt the pain of navigation setup. It is easy to get wrong and hard to keep clean as apps grow. This article explains what routing means, why it matters,

Modern apps are not just a pile of screens. They are a living system of features, data flows, offline behavior, and performance constraints. If you have only shipped small apps, the jump to something

Your application has user accounts. Some routes should only be accessible to logged-in users. How do you protect routes and verify that a request actually comes from who they claim to be? JWT (JSON We

Ashish's Blog
57 posts
Users need to upload files to your application. Maybe they're uploading profile pictures, documents, or media. Once you store these files, you need to serve them back to users. This requires understanding where files go and how to make them accessible.
When a user uploads a file, it doesn't magically appear on the web. You need to decide where to store it.
There are two main approaches:
Local Storage (on your server): Files are saved to a folder on your server's disk. Your server then serves these files directly to users.
External Storage (cloud): Files are uploaded to a service like AWS S3, Google Cloud Storage, or Cloudinary. Your server doesn't store them; the cloud service does.
We'll focus on local storage first, as it's simpler to understand and perfect for learning.
Create a folder where uploads will be stored:
project/
uploads/
server.js
package.json
This uploads folder holds all user files.
Express doesn't handle file uploads by default. You need middleware like Multer.
First, install Multer:
npm install multer
Here's how to set up basic file uploads:
import express from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';
const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// Configure storage
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 });
app.post('/upload', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).send('No file uploaded');
}
res.json({ message: 'File uploaded', filename: req.file.filename });
});
app.listen(3000, () => console.log('Server running'));
When a user uploads a file:
Multer intercepts the request
Saves the file to the uploads/ folder with a unique name
Adds file information to req.file
Your route handler processes the request
Now users need to access these files. You'll serve them as static files.
Express has a built-in middleware for this:
import express from 'express';
import path from 'path';
import { fileURLToPath } from 'url';
const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// Serve files from uploads folder
app.use('/files', express.static(path.join(__dirname, 'uploads')));
app.listen(3000, () => console.log('Server running'));
Now files are accessible at http://localhost:3000/files/filename.jpg
If a file is saved as profile-1234567890.jpg in the uploads/ folder, it's accessible at:
http://localhost:3000/files/profile-1234567890.jpg
Here's a complete example with upload and serving:
import express from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';
const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// Configure storage
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 });
// Serve uploaded files as static
app.use('/files', express.static(path.join(__dirname, 'uploads')));
app.post('/upload-profile', upload.single('profilePic'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded' });
}
const fileUrl = `/files/${req.file.filename}`;
res.json({
message: 'Profile picture uploaded',
url: fileUrl
});
});
app.listen(3000, () => console.log('Server running'));
A user uploads a file → Multer saves it → Server returns the file URL → User can access it immediately.
Client Server Disk
| | |
|--- POST /upload (file) -----> | |
| (multipart/form-data) | |
| |-- Multer processes ------>|
| | saves file |
| | |
|<-- 200 OK + filename ---------| |
| { url: '/files/...' } | |
| | |
|-- GET /files/filename.jpg --> | |
| |-- Read from disk ------> |
|<-- 200 + file data -----------|<-- Return file data ------|
Validate file types:
const upload = multer({
storage,
fileFilter: (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Invalid file type'));
}
}
});
Limit file size:
const upload = multer({
storage,
limits: { fileSize: 5 * 1024 * 1024 } // 5MB
});
Never trust the original filename:
Always generate a new filename (like we did with timestamps and random numbers). If you use the original filename, malicious users could upload files with paths like ../../important.js to write files outside the uploads folder.
Usually, you'll want to store information about the uploaded file:
import express from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';
const app = express();
const __dirname = path.dirname(fileURLToPath(import.meta.url));
app.use(express.json());
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 });
app.use('/files', express.static(path.join(__dirname, 'uploads')));
app.post('/upload', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded' });
}
// Store metadata about the file
const fileData = {
originalName: req.file.originalname,
savedName: req.file.filename,
size: req.file.size,
mimetype: req.file.mimetype,
uploadedAt: new Date(),
url: `/files/${req.file.filename}`
};
// You'd normally save fileData to a database
res.json(fileData);
});
app.listen(3000, () => console.log('Server running'));
Store this information in a database so you can track what was uploaded, when, and by whom.
Mistake 1: Using original filenames
Never save files with their original names. Attackers can upload files with special characters or path traversal sequences.
Mistake 2: Not validating file types
Don't just check the file extension. Someone can rename malware.exe to image.jpg. Always check the MIME type.
Mistake 3: Storing sensitive files in the web-accessible folder
Files served via express.static are publicly accessible. Don't store passwords, API keys, or sensitive data there.
Multer middleware handles file uploads in Express
Files are stored to disk in a designated folder
Use express.static middleware to serve uploaded files
Generate unique filenames to prevent security issues
Validate file types and sizes
Store file metadata in a database
Never trust the original filename provided by users
Once you're comfortable with local storage, you can explore cloud storage solutions for better scalability and reliability.