Skip to main content

Command Palette

Search for a command to run...

Storing Uploaded Files and Serving Them in Express

Updated
6 min read

Handling file uploads is one of those features every backend developer eventually builds—whether it’s profile pictures, PDFs, videos, or datasets. In a Node.js application using Express.js, this involves two key responsibilities:

  1. Accepting and storing uploaded files

  2. Serving those files back to users efficiently and securely

This detailed blog walks you through the entire process—from setup to best practices—so you can implement file handling in real-world applications.

Why File Upload Handling Matters

Modern applications rely heavily on user-generated content:

  • Social media apps → images & videos

  • Job portals → resumes (PDFs)

  • E-learning platforms → assignments

  • Analytics systems → datasets

Without proper file handling:

  • Your server can crash due to large uploads

  • Security vulnerabilities can arise

  • Files may not be accessible properly


Prerequisites

Before we begin, ensure you have:

  • Basic knowledge of Node.js and Express

  • npm installed

  • A project initialized


Step 1: Setting Up the Project

Create a new project:

mkdir file-upload-app
cd file-upload-app
npm init -y

Install dependencies:

npm install express multer

Here:

  • express → Web framework

  • multer → Middleware for handling file uploads


Step 2: Project Structure

A clean structure helps maintain scalability:

file-upload-app/
│
├── uploads/        # Stored files
├── public/         # Static assets
├── app.js          # Main server file
└── package.json

Step 3: Basic Express Server

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Server running on port 3000");
});

Step 4: Handling File Uploads with Multer

Multer is the most popular library for handling multipart/form-data.

Basic Setup

const multer = require("multer");

// Storage configuration
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 });

Step 5: Upload Route

app.post("/upload", upload.single("file"), (req, res) => {
  res.send("File uploaded successfully!");
});

Explanation:

  • upload.single("file") → Handles one file

  • "file" → Name attribute in HTML form


HTML Form for Upload

<form action="/upload" method="POST" enctype="multipart/form-data">
  <input type="file" name="file" />
  <button type="submit">Upload</button>
</form>

Step 6: Storing Files

Files are stored in the uploads/ directory.

Example filename:

1714828392-myfile.png

Why rename files?

  • Avoid duplicates

  • Improve traceability

  • Prevent overwriting


Step 7: Serving Uploaded Files

Now comes the second part—serving files to users.

Static Serving

app.use("/uploads", express.static("uploads"));

Now any file can be accessed via:

http://localhost:3000/uploads/filename.png

Example

If a file is stored as:

uploads/1714828392-photo.jpg

You can access it at:

http://localhost:3000/uploads/1714828392-photo.jpg

Step 8: File Validation & Security

File uploads are a major security risk if not handled properly.

1. File Type Validation

const fileFilter = (req, file, cb) => {
  if (file.mimetype === "image/png" || file.mimetype === "image/jpeg") {
    cb(null, true);
  } else {
    cb(new Error("Only images allowed!"), false);
  }
};

const upload = multer({
  storage: storage,
  fileFilter: fileFilter
});

2. File Size Limit

const upload = multer({
  storage: storage,
  limits: { fileSize: 2 * 1024 * 1024 } // 2MB
});

3. Preventing Malicious Files

Avoid:

  • .exe, .sh, .bat files

  • Unknown MIME types

Use libraries like:

  • helmet

  • express-validator


Step 9: Multiple File Uploads

app.post("/uploads", upload.array("files", 5), (req, res) => {
  res.send("Multiple files uploaded!");
});
  • "files" → input name

  • 5 → max number of files

Step 10: Uploading Different File Types

Example: Images + PDFs

const fileFilter = (req, file, cb) => {
  const allowedTypes = ["image/jpeg", "image/png", "application/pdf"];
  
  if (allowedTypes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error("Invalid file type"), false);
  }
};

Step 11: Storing Files in Cloud (Advanced)

Instead of storing locally, you can use:

  • Amazon S3

  • Cloudinary

Benefits:

  • Scalability

  • Better performance

  • CDN support

Step 12: Serving Files Securely

Sometimes you don’t want files publicly accessible.

Controlled Access Route

app.get("/file/:name", (req, res) => {
  const filePath = `uploads/${req.params.name}`;
  res.sendFile(__dirname + "/" + filePath);
});

Add Authentication

You can restrict access using:

  • JWT authentication

  • Session-based login

Step 13: Performance Optimization

1. Use Streaming

const fs = require("fs");

app.get("/stream/:name", (req, res) => {
  const stream = fs.createReadStream(`uploads/${req.params.name}`);
  stream.pipe(res);
});

2. Use Caching

app.use("/uploads", express.static("uploads", {
  maxAge: "1d"
}));

Step 14: Testing File Uploads

Use tools like:

  • Postman

  • cURL

Step 15: Real-World Use Cases

1. Social Media App

  • Upload profile pictures

  • Serve images via CDN

2. Resume Portal

  • Upload PDF resumes

  • Restrict access to recruiters

3. Data Analytics Platform

  • Upload CSV datasets

  • Process files asynchronously


Common Mistakes

❌ 1. Not validating files

Leads to security issues

❌ 2. Storing files in root directory

Always use separate folders

❌ 3. No size limits

Can crash your server

❌ 4. Exposing sensitive files publicly

Use controlled routes


Best Practices

  • Always validate file type and size

  • Use unique filenames

  • Store metadata in a database

  • Use cloud storage for production

  • Implement authentication for access control


Conclusion

Handling file uploads and serving them efficiently is a critical backend skill. With Express.js and Multer, you can build robust systems that:

  • Accept user files securely

  • Store them efficiently

  • Serve them reliably

As your application scales, consider moving to cloud storage solutions and adding advanced security layers.


Final Thoughts

Mastering file uploads in Node.js opens doors to building real-world applications like:

  • Instagram-like platforms

  • E-learning systems

  • SaaS dashboards