https://github.com/iamskyy666/file-upload-nodejs
📂 All about file/image uploading using NodeJs/ExpressJs 🟢
https://github.com/iamskyy666/file-upload-nodejs
backend cloudinary expressjs file-upload image-upload multer nodejs
Last synced: 4 days ago
JSON representation
📂 All about file/image uploading using NodeJs/ExpressJs 🟢
- Host: GitHub
- URL: https://github.com/iamskyy666/file-upload-nodejs
- Owner: iamskyy666
- Created: 2026-06-22T07:02:15.000Z (7 days ago)
- Default Branch: main
- Last Pushed: 2026-06-22T09:36:58.000Z (6 days ago)
- Last Synced: 2026-06-22T11:20:45.487Z (6 days ago)
- Topics: backend, cloudinary, expressjs, file-upload, image-upload, multer, nodejs
- Language: JavaScript
- Homepage:
- Size: 486 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.MD
Awesome Lists containing this project
README
# File Uploading in Node.js and Express 📂
---
# 1. The Fundamental Question
Suppose we have a form:
```html
```
and we choose:
```text
profile.jpg
```
Question:
**How does an image travel from our computer to the server?**
---
# 2. Why Can't We Send Files as JSON?
Normally we send:
```json
{
"username": "Skyy",
"email": "abc@gmail.com"
}
```
using:
```http
Content-Type: application/json
```
This works because:
```text
JSON = Text
```
---
But an image is:
```text
Binary Data
```
Example:
```text
01010010
11101001
00110110
...
```
JSON isn't designed to efficiently transmit large binary files.
Therefore:
```text
application/json
```
is not appropriate.
---
# 3. The Solution: multipart/form-data
Browsers use:
```http
Content-Type: multipart/form-data
```
This means:
```text
Split request into multiple parts
```
Example:
```text
Part 1:
username=Skyy
Part 2:
file=profile.jpg
```
---
A request looks something like:
```http
POST /upload
Content-Type: multipart/form-data
------Boundary
Content-Disposition: form-data; name="username"
Skyy
------Boundary
Content-Disposition: form-data;
name="avatar";
filename="profile.jpg"
(binary image data)
------Boundary--
```
---
# Why the Boundary?
Imagine:
```text
username
image
address
resume
```
all inside one request.
The boundary acts like:
```text
---------- Separator ----------
```
so the server knows:
```text
Where one field ends
and another begins
```
---
# Why Doesn't Express Parse Files Automatically?
We know:
```js
app.use(express.json());
```
This parses:
```text
application/json
```
And:
```js
app.use(express.urlencoded());
```
parses:
```text
x-www-form-urlencoded
```
But:
```text
multipart/form-data
```
is much more complicated.
It may contain:
* Images
* Videos
* PDFs
* ZIP files
* Large binary streams
Express deliberately does not parse this.
---
Therefore we use:
```text
multer
```
or
```text
formidable
```
or
```text
busboy
```
---
# What Does Multer Actually Do?
Many tutorials say:
```bash
npm install multer
```
and stop there.
But internally:
Multer:
```text
Reads incoming request stream
↓
Separates boundaries
↓
Extracts files
↓
Extracts text fields
↓
Stores files
↓
Attaches them to req.file
```
---
Example:
```js
upload.single("avatar")
```
This middleware:
```text
Reads multipart request
↓
Finds field named avatar
↓
Saves file
↓
Adds req.file
```
---
Then:
```js
console.log(req.file)
```
might print:
```js
{
fieldname: 'avatar',
originalname: 'profile.jpg',
mimetype: 'image/jpeg',
size: 50382,
filename:
'1745214142-profile.jpg',
path:
'uploads/profile.jpg'
}
```
---
# File Upload Middleware
Consider:
```js
app.post(
"/upload",
upload.single("avatar"),
controller
)
```
Execution:
```text
Request arrives
↓
Multer middleware
↓
Reads image
↓
Stores image
↓
Adds req.file
↓
Calls controller
↓
Controller saves DB record
```
---
# upload.single()
Means:
```text
Accept exactly one file
```
---
Example:
```html
```
---
Server:
```js
upload.single("avatar")
```
---
Then:
```js
req.file
```
contains:
```text
One file object
```
---
# upload.array()
Suppose:
```html
```
---
Server:
```js
upload.array("photos",5)
```
---
Now:
```js
req.files
```
contains:
```text
[
img1.jpg,
img2.jpg,
img3.jpg
]
```
Maximum:
```text
5 files
```
---
# upload.fields()
Suppose:
```html
avatar
resume
coverImage
```
Three different files.
---
We can do:
```js
upload.fields([
{ name: "avatar" },
{ name: "resume" },
{ name: "coverImage" }
])
```
---
Result:
```js
req.files.avatar
req.files.resume
req.files.coverImage
```
---
# Where Are Files Stored?
This is an important design decision.
There are generally three approaches.
---
# Option 1
Store on local disk.
Example:
```text
uploads/
profile.jpg
resume.pdf
```
---
Multer:
```js
destination:
"./uploads"
```
---
Advantages:
Simple.
---
Disadvantages:
If server crashes:
```text
Files disappear
```
---
If we run:
```text
Server A
Server B
Server C
```
Files are inconsistent.
---
# Option 2
Store inside database.
Example:
```text
MongoDB
image:
```
---
Possible.
But:
```text
Database size grows rapidly.
```
Usually not recommended for large images.
---
# Option 3 (Industry Standard)
Store files in cloud storage.
Examples:
* [Amazon S3](https://aws.amazon.com/s3/?utm_source=chatgpt.com)
* [Cloudinary](https://cloudinary.com/?utm_source=chatgpt.com)
* [Firebase Storage](https://firebase.google.com/products/storage?utm_source=chatgpt.com)
---
The flow:
```text
Browser
↓
Express
↓
Cloud Storage
↓
Receive URL
↓
Save URL to MongoDB
```
---
Database:
```json
{
"username":"Skyy",
"avatar":
"https://..."
}
```
Not the image itself.
---
# Image Upload Flow in MERN
A very common architecture:
```text
React
↓
Select Image
↓
FormData
↓
Axios POST
↓
Express
↓
Multer
↓
Cloudinary
↓
Image URL
↓
MongoDB
↓
Return Response
```
---
# Why Use FormData?
Suppose:
```html
```
We get:
```js
const file = e.target.files[0]
```
---
Then:
```js
const formData = new FormData();
formData.append(
"avatar",
file
);
```
---
Axios:
```js
axios.post(
"/upload",
formData
)
```
Browser automatically sets:
```http
multipart/form-data
```
---
# req.body vs req.file
Suppose:
```html
```
---
After multer:
```js
req.body
```
contains:
```js
{
name:"Skyy"
}
```
---
while:
```js
req.file
```
contains:
```js
{
originalname:"profile.jpg"
}
```
---
# File Size Limits
Never trust users.
Someone may upload:
```text
50GB video
```
---
Multer allows:
```js
limits:{
fileSize:
1024*1024*5
}
```
Meaning:
```text
5MB max
```
---
# File Type Validation
Suppose attacker uploads:
```text
virus.exe
```
renamed as:
```text
image.jpg
```
Dangerous.
---
Therefore:
```js
file.mimetype
```
is checked.
Allowed:
```text
image/png
image/jpeg
image/webp
```
Rejected:
```text
application/exe
```
---
# Why Images Are Special
Images are often:
```text
Compressed
Resized
Optimized
Converted
```
Example:
User uploads:
```text
15MB PNG
```
Server converts:
```text
600KB WEBP
```
Saving:
* Storage
* Bandwidth
* Loading time
---
# Signed URLs
Modern systems often don't let:
```text
Browser
↓
Express
↓
S3
```
Instead:
```text
Browser
↓
Ask Express
↓
Get Signed URL
↓
Upload directly to S3
```
---
Advantages:
```text
Server handles zero image data.
Less CPU
Less RAM
Better scalability
```
---
# Memory Storage vs Disk Storage
Multer offers:
---
## Disk Storage
```text
Image
↓
uploads/
↓
File Path
```
---
## Memory Storage
```text
Image
↓
RAM Buffer
↓
Cloudinary
↓
Delete Buffer
```
---
Cloud uploads often use:
```text
Memory Storage
```
because:
```text
No temporary file
```
is created.
---
# Complete Mental Model
When we upload an image:
```text
User selects file
↓
Browser creates FormData
↓
multipart/form-data request
↓
Express receives stream
↓
Multer parses stream
↓
Separates text and files
↓
Stores file
↓
Creates req.file
↓
Controller runs
↓
Upload to Cloudinary/S3
↓
Get URL
↓
Save URL to MongoDB
↓
Send response
```
# Industry Standard Today
For modern MERN applications, the most common architecture is:
```text
React
↓
FormData
↓
Express
↓
Multer (Memory Storage)
↓
Cloudinary / Amazon S3
↓
MongoDB stores URL only
```
This architecture is scalable, inexpensive, cloud-friendly, and is the approach we will encounter in many professional Node.js and Express applications.
Let's study file uploading from the **code level**, but still understand *why* each piece exists. We'll use the most common setup:
```text
React / HTML Form
↓
multipart/form-data
↓
Express
↓
Multer
↓
Local Storage or Cloudinary
↓
MongoDB stores URL
```
---
# 1. Install Multer
First:
```bash
npm install multer
```
Why?
Because:
```text
express.json()
```
can parse:
```text
application/json
```
and
```text
express.urlencoded()
```
can parse:
```text
application/x-www-form-urlencoded
```
But:
```text
multipart/form-data
```
contains:
* binary images
* videos
* files
* text fields
Express cannot parse this by itself.
Multer is the parser.
---
# 2. Create Upload Middleware
Example:
```js
import multer from "multer";
```
Now:
```js
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./uploads");
},
filename: function (req, file, cb) {
cb(
null,
Date.now() + "-" + file.originalname
);
},
});
```
---
## What's happening here?
### destination
```js
destination(req,file,cb)
```
determines:
```text
Where should we store the file?
```
Example:
```text
uploads/
```
---
### cb()
Multer uses:
```js
cb(error, result)
```
Example:
```js
cb(null, "./uploads");
```
means:
```text
No error
Store file in:
./uploads
```
---
# filename
Suppose two users upload:
```text
avatar.jpg
```
If we store:
```text
uploads/avatar.jpg
```
second file overwrites first.
Bad.
---
So:
```js
Date.now()
```
creates:
```text
172000000-avatar.jpg
```
Unique filename.
---
# 3. Create Upload Instance
```js
const upload = multer({
storage,
});
```
This creates middleware.
---
# 4. upload.single()
Example:
```js
router.post(
"/upload",
upload.single("avatar"),
uploadController
);
```
Question:
Why `"avatar"`?
Because:
```html
```
The field names must match.
---
When request arrives:
```text
upload.single("avatar")
↓
Find field named avatar
↓
Store image
↓
Attach to req.file
↓
Call controller
```
---
# 5. req.file
Suppose:
```text
profile.jpg
```
was uploaded.
Then:
```js
console.log(req.file)
```
prints:
```js
{
fieldname: "avatar",
originalname:
"profile.jpg",
encoding: "7bit",
mimetype:
"image/jpeg",
destination:
"./uploads",
filename:
"17238932-profile.jpg",
path:
"uploads/17238932-profile.jpg",
size:
54000
}
```
---
Let's understand each field.
---
### originalname
```text
User uploaded:
profile.jpg
```
---
### filename
Stored as:
```text
17238932-profile.jpg
```
---
### path
Where file physically exists:
```text
uploads/17238932-profile.jpg
```
---
### mimetype
Very important:
```text
image/jpeg
```
or
```text
image/png
```
We can validate uploads using this.
---
# 6. req.body still works
Suppose:
```html
```
After multer:
```js
req.body
```
contains:
```js
{
username:"Skyy"
}
```
while:
```js
req.file
```
contains:
```js
{
originalname:
"profile.jpg"
}
```
---
# 7. upload.array()
Suppose:
```html
```
User uploads:
```text
1.jpg
2.jpg
3.jpg
```
---
Server:
```js
upload.array("photos",5)
```
Meaning:
```text
Field:
photos
Maximum:
5 files
```
---
Result:
```js
req.files
```
contains:
```js
[
{...},
{...},
{...}
]
```
---
# 8. upload.fields()
Suppose:
Our form:
```text
avatar
resume
coverImage
```
---
Server:
```js
upload.fields([
{
name:"avatar",
maxCount:1
},
{
name:"resume",
maxCount:1
},
{
name:"coverImage",
maxCount:1
}
])
```
---
Result:
```js
req.files.avatar
req.files.resume
req.files.coverImage
```
---
# 9. File Size Limits
Never trust uploads.
Someone may upload:
```text
10GB movie
```
Server RAM dies.
---
Multer:
```js
const upload = multer({
storage,
limits: {
fileSize:
5*1024*1024
}
});
```
---
This means:
```text
5MB max
```
---
If exceeded:
Multer throws:
```text
LIMIT_FILE_SIZE
```
---
# 10. File Type Validation
Suppose attacker uploads:
```text
virus.exe
```
renamed:
```text
cat.jpg
```
Dangerous.
---
We use:
```js
fileFilter
```
---
Example:
```js
const upload = multer({
storage,
fileFilter:
(req,file,cb)=>{
if(
file.mimetype ===
"image/jpeg"
){
cb(null,true);
}
else{
cb(
new Error(
"Only images"
)
);
}
}
})
```
---
Allowed:
```text
image/png
image/jpeg
image/webp
```
Rejected:
```text
application/exe
```
---
# 11. Cloudinary Upload
In production:
We usually don't keep:
```text
uploads/
```
because:
```text
Server restarts
↓
Files disappear
```
---
Instead:
```text
Express
↓
Cloudinary
↓
Image URL
```
---
Code:
```js
const result =
await cloudinary.uploader.upload(
req.file.path
)
```
---
Cloudinary returns:
```js
{
public_id:
"abc123",
secure_url:
"https://..."
}
```
---
Then:
```js
await User.create({
avatar:
result.secure_url
})
```
---
MongoDB stores:
```json
{
"avatar":
"https://res.cloudinary..."
}
```
---
Not:
```text
Binary image
```
Only:
```text
URL
```
---
# 12. Memory Storage
Instead of:
```text
Image
↓
uploads folder
↓
Cloudinary
```
we can do:
```text
Image
↓
RAM Buffer
↓
Cloudinary
```
---
Code:
```js
const storage =
multer.memoryStorage();
```
---
Then:
```js
req.file.buffer
```
contains:
```text
Raw binary image
```
inside RAM.
---
Very common with:
```text
Cloudinary
Amazon S3
Firebase Storage
```
---
# 13. Serving Static Images
Suppose:
```text
uploads/
profile.jpg
```
We want:
```text
localhost:3000/profile.jpg
```
---
Express:
```js
app.use(
express.static("uploads")
)
```
---
Now:
```text
GET
/profile.jpg
```
returns:
```text
Actual image
```
---
# 14. React Side
Suppose:
```html
```
---
We get:
```js
const file
=
e.target.files[0]
```
---
Create:
```js
const formData
=
new FormData()
```
---
Append:
```js
formData.append(
"avatar",
file
)
```
---
Send:
```js
await axios.post(
"/upload",
formData
)
```
---
Do NOT do:
```js
headers:{
'Content-Type':
'multipart/form-data'
}
```
Axios and the browser automatically generate:
```text
multipart/form-data
+
boundary
```
for us.
---
# The Entire Request Lifecycle
```text
Choose image
↓
Browser File Object
↓
FormData
↓
multipart/form-data
↓
Express
↓
Multer middleware
↓
req.file
↓
Cloudinary upload
↓
Receive secure_url
↓
Store URL in MongoDB
↓
Return response
↓
React displays image
```
# A Production MERN Upload Architecture
Most professional MERN applications eventually evolve toward:
```text
React
↓
FormData
↓
Axios
↓
Express
↓
Multer Memory Storage
↓
Cloudinary / S3
↓
MongoDB stores image URL only
↓
Frontend renders URL
```
This is the architecture used in countless production applications because it avoids storing massive binary files in MongoDB, scales horizontally, and keeps our Node.js server lightweight and efficient.
---
# Multer Vs Express-fileupload 📁
This is an excellent question because many beginners see both **Multer** and **express-fileupload** and wonder why both exist.
The short answer is:
> We usually **don't need both**. They solve the same problem in different ways.
---
# The Problem Both Libraries Solve
Suppose our frontend sends:
```html
```
The browser sends:
```http
Content-Type: multipart/form-data
```
along with the file bytes.
---
Express can parse:
```js
app.use(express.json());
```
for:
```text
application/json
```
and:
```js
app.use(express.urlencoded({ extended: true }));
```
for:
```text
application/x-www-form-urlencoded
```
But Express **cannot parse**:
```text
multipart/form-data
```
which is used for:
* Images
* PDFs
* Videos
* ZIP files
Therefore we need a middleware.
---
# Option 1: express-fileupload
Install:
```bash
npm install express-fileupload
```
Use:
```js
import fileUpload from "express-fileupload";
app.use(fileUpload());
```
Now:
```js
req.files
```
becomes available automatically.
---
Suppose:
```html
```
Then:
```js
console.log(req.files);
```
might show:
```js
{
image: {
name: "cat.jpg",
mimetype: "image/jpeg",
size: 45213,
data:
}
}
```
---
# Saving the File
With express-fileupload:
```js
await req.files.image.mv(
"./uploads/cat.jpg"
);
```
The `.mv()` method means:
```text
Move uploaded file
to another location.
```
---
# Example
```js
const uploadImage = async (req,res)=>{
const image = req.files.image;
await image.mv(
"./uploads/" + image.name
);
res.send("Uploaded");
}
```
Very simple.
---
# Why Do Tutorials Use express-fileupload?
Because:
```text
Easy to learn
```
A beginner can upload files in:
```text
5 minutes
```
without learning:
* Storage engines
* Streams
* Buffers
* DiskStorage
* MemoryStorage
---
# But There Are Limitations
Imagine users upload:
```text
50 MB
100 MB
500 MB videos
```
---
express-fileupload often:
```text
Loads file into memory first.
```
Meaning:
```text
Request
↓
RAM
↓
Disk
```
Large files consume memory.
---
If:
```text
100 users
×
100MB uploads
```
Server RAM can explode.
---
# Option 2: Multer
Multer is more advanced.
Instead of:
```text
Whole file into RAM
```
Multer can:
```text
Receive stream
↓
Write directly to disk
↓
Continue receiving
```
This is:
```text
Streaming
```
Much more efficient.
---
# Multer Architecture
```text
Browser
↓
multipart request
↓
Multer
↓
DiskStorage
↓
uploads/
↓
Controller
```
---
Or:
```text
Browser
↓
Multer
↓
Memory Buffer
↓
Cloudinary
↓
MongoDB URL
```
---
# Why Production Apps Prefer Multer
Multer gives us:
### 1. Single File
```js
upload.single("avatar")
```
---
### 2. Multiple Files
```js
upload.array("images",5)
```
---
### 3. Different File Fields
```js
upload.fields([
{name:"avatar"},
{name:"resume"}
])
```
---
### 4. File Limits
```js
limits:{
fileSize:
5*1024*1024
}
```
5MB maximum.
---
### 5. File Type Validation
```js
fileFilter
```
Example:
```js
if(
file.mimetype==="image/jpeg"
)
```
Reject:
```text
virus.exe
```
Pretending to be:
```text
cat.jpg
```
---
# Why express-fileupload Is Still Popular
Because this:
```js
const image = req.files.image;
await image.mv("./uploads/" + image.name);
```
is incredibly easy.
---
No:
```text
storage
memoryStorage
diskStorage
fileFilter
upload.single
```
---
Many small tutorials and prototypes use it.
---
# Why Some E-commerce Courses Use express-fileupload
Because the instructor wants to teach:
```text
Products
Orders
JWT
MongoDB
Redux
```
and not spend:
```text
2 hours
```
explaining:
```text
Streams
Buffers
Storage Engines
Multipart Parsing
```
---
# express-fileupload vs Multer
| Feature | express-fileupload | Multer |
| -------------------- | ------------------ | ---------- |
| Beginner Friendly | ✅ Very | Moderate |
| Single File | ✅ | ✅ |
| Multiple Files | ✅ | ✅ |
| Memory Efficient | ❌ | ✅ |
| Streams | ❌ | ✅ |
| Cloud Uploads | Possible | Excellent |
| Production Ready | Small apps | Large apps |
| Fine-Grained Control | Limited | Excellent |
---
# Which One Should We Learn?
Since we are learning:
* Node.js
* Express
* MERN
* Professional backend development
I would recommend:
```text
Multer
```
because:
```text
Multer
↓
Memory Storage
↓
Cloudinary
↓
MongoDB stores URL
↓
React displays image
```
This architecture is extremely common in professional MERN applications.
---
# Then Why Learn express-fileupload at All?
Because we may encounter older tutorials or codebases that use:
```js
app.use(fileUpload())
```
and:
```js
req.files.image.mv(...)
```
Understanding it helps us read and maintain those projects, even if for new projects we would usually prefer Multer with cloud storage.