# Storing Uploaded Files and Serving Them in Express

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.

## Where Do Uploaded Files Live?

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.

## Setting Up a Local Upload Folder

Create a folder where uploads will be stored:

```plaintext
project/
  uploads/
  server.js
  package.json
```

This `uploads` folder holds all user files.

## Handling File Uploads with Multer

Express doesn't handle file uploads by default. You need middleware like Multer.

First, install Multer:

```bash
npm install multer
```

Here's how to set up basic file uploads:

```javascript
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:

1.  Multer intercepts the request
    
2.  Saves the file to the `uploads/` folder with a unique name
    
3.  Adds file information to `req.file`
    
4.  Your route handler processes the request
    

## Serving Uploaded Files

Now users need to access these files. You'll serve them as static files.

Express has a built-in middleware for this:

```javascript
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:

```plaintext
http://localhost:3000/files/profile-1234567890.jpg
```

## Complete Upload and Serve Example

Here's a complete example with upload and serving:

```javascript
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.

## Upload Process Visualization

```plaintext
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 ------|
```

## Security Considerations

**Validate file types:**

```javascript
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:**

```javascript
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.

## Storing Metadata

Usually, you'll want to store information about the uploaded file:

```javascript
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.

## Common Mistakes

**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.

## Key Takeaways

*   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.
