An open API service indexing awesome lists of open source software.

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 🟢

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.