Skip to main content

Command Palette

Search for a command to run...

Storing Uploaded Files and Serving Them in Express

Updated
7 min readView as Markdown

File uploads are a core part of many modern web applications. Profile pictures, PDFs, resumes, product images, invoices, videos, and documents all rely on proper file storage and delivery systems.

In an Express.js application, uploading a file is only half the job. You also need to decide:

  • Where should the file be stored?

  • How should users access it?

  • How do you serve files securely?

  • Should you use local storage or external cloud storage?

This blog explains how file storage works in Express, how static file serving works, and the best practices for handling uploaded files safely.


Understanding File Uploads in Express

When a user uploads a file from the frontend, the file is sent to the backend using a multipart/form-data request.

Express itself does not handle file uploads directly. Libraries such as:

  • multer

  • busboy

  • formidable

are commonly used to process uploaded files.

A typical upload flow looks like this:

  1. User selects a file

  2. Browser sends file to Express server

  3. Express receives the file

  4. File is stored in a folder or cloud storage

  5. Server returns the file URL

  6. Users access the file through that URL


Where Uploaded Files Are Stored

Uploaded files can be stored in different locations depending on the application requirements.

1. Local File Storage

In local storage, files are saved directly inside the project folder on the server.

Example structure:

project/
│
├── uploads/
│   ├── image1.png
│   ├── resume.pdf
│   └── video.mp4
│
├── src/
├── public/
└── 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 });

Advantages

  • Easy to set up

  • Good for small projects

  • Fast local access

  • No external service required

Disadvantages

  • Files can be lost if server crashes

  • Difficult to scale across multiple servers

  • Large uploads consume server disk space

  • Not ideal for production-scale systems


Local Storage vs External Storage

As applications grow, storing files locally becomes harder to manage.

Local Storage

Files remain inside the server machine.

Best For

  • Learning projects

  • Small applications

  • Development environments

  • Internal tools

Challenges

  • Limited storage capacity

  • Backup management

  • Server migration issues

  • Difficult horizontal scaling


External Storage

Files are stored in dedicated cloud storage services.

Popular services include:

  • Amazon Web Services S3

  • Cloudinary

  • Google Cloud Storage

  • Firebase Storage

Advantages

  • Highly scalable

  • Better reliability

  • CDN support

  • Automatic backups

  • Optimized file delivery

Disadvantages

  • Additional cost

  • Slightly more setup complexity

  • Requires API configuration


Serving Static Files in Express

Once files are stored, users need a way to access them.

This is where static file serving comes in.

Express provides built-in middleware called express.static() for serving files publicly.

Example:

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

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

This line means:

Physical Folder: uploads/
URL Route: /uploads

How Static File Serving Works

Suppose your folder contains:

uploads/profile.png

And Express uses:

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

Then the file becomes accessible at:

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

The browser sends a request to the URL, and Express directly serves the file from the folder.


Accessing Uploaded Files via URL

A common pattern is returning the uploaded file URL after upload.

Example upload response:

{
  "message": "Upload successful",
  "fileUrl": "/uploads/1715234123-profile.png"
}

Frontend applications can then use:

<img src="/uploads/1715234123-profile.png" />

Or:

window.open('/uploads/1715234123-profile.png');

This makes uploaded files accessible just like normal web resources.


Complete Example with Multer and Static Files

Install Dependencies

npm install express multer

Folder Structure

project/
│
├── uploads/
├── server.js
└── package.json

server.js

const express = require('express');
const multer = require('multer');

const app = express();

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 });

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

app.post('/upload', upload.single('file'), (req, res) => {
  res.json({
    message: 'File uploaded successfully',
    fileUrl: `/uploads/${req.file.filename}`
  });
});

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

Security Considerations for File Uploads

File uploads are a major security risk if handled carelessly.

Never trust uploaded files blindly.


1. Validate File Types

Attackers may upload malicious files like:

  • .exe

  • .php

  • .bat

  • scripts containing malware

Restrict allowed file types.

Example:

const fileFilter = (req, file, cb) => {
  const allowedTypes = ['image/png', 'image/jpeg'];

  if (allowedTypes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error('Invalid file type'));
  }
};

2. Limit File Size

Large uploads can crash the server or consume excessive storage.

Example:

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

This limits uploads to 2 MB.


3. Rename Uploaded Files

Never store files using the original filename directly.

Bad:

resume.pdf

Better:

1715234123-resume.pdf

Or use UUIDs.

This prevents filename conflicts and reduces risks.


4. Store Uploads Outside Sensitive Directories

Do not store uploads inside:

src/
config/
node_modules/

Use a dedicated upload folder.


5. Prevent Executable File Access

Avoid allowing uploaded files to execute as code.

For example:

  • Do not execute uploaded scripts

  • Restrict server permissions

  • Serve uploads as static content only


6. Scan Files in Production Systems

Enterprise applications often use antivirus scanning before storing files permanently.

This is especially important for:

  • PDF uploads

  • Document uploads

  • Public platforms

  • Enterprise systems


A cleaner structure improves maintainability.

project/
│
├── public/
│
├── uploads/
│   ├── images/
│   ├── documents/
│   └── videos/
│
├── routes/
├── controllers/
├── middleware/
└── server.js

Benefits:

  • Easier organization

  • Better scalability

  • Cleaner separation of concerns


When to Use Local Storage vs Cloud Storage

Requirement Recommended Option
Small learning project Local storage
Portfolio project Local storage
Production SaaS app Cloud storage
Large media uploads Cloud storage
Multiple servers Cloud storage
Temporary files Local storage
High scalability Cloud storage

Best Practices for File Handling

  • Validate file types

  • Limit upload size

  • Rename uploaded files

  • Organize uploads by folders

  • Use cloud storage for scalability

  • Protect sensitive uploads

  • Remove unused files regularly

  • Use environment variables for storage configs


Common Mistakes Developers Make

Storing Everything Publicly

Not all uploads should be publicly accessible.

Sensitive documents should require authentication.


Ignoring File Validation

This creates security vulnerabilities quickly.


Using Original Filenames

Filename collisions become inevitable in real applications.


Keeping Large Files on Small Servers

This eventually causes storage and performance issues.


Final Thoughts

Uploading files in Express is straightforward, but handling them correctly requires proper planning.

You need to think beyond just receiving the file:

  • Where will it be stored?

  • How will users access it?

  • How will you secure it?

  • Can the system scale later?

For small applications, local folder storage with express.static() works well and is easy to understand.

For production systems handling large amounts of data, cloud storage solutions provide better scalability, reliability, and performance.

A strong upload system focuses on:

  • Organization

  • Security

  • Accessibility

  • Scalability

  • Performance