# File Handling

Course: [Express.js](https://www.devtutorials.ai/learn/expressjs) · Section: Data Handling
Source: https://www.devtutorials.ai/learn/expressjs/data-handling/file-handling

> Sending files with res.sendFile, downloads with res.download and res.attachment, streaming large files, uploads with Multer, memory and disk storage, checking uploads, path traversal and other file security, and upload error handling.

## File Handling Introduction

Servers often send files and receive files.

- **Sending:** a menu PDF, a report card, a photo.
- **Downloading:** the browser saves the file instead of showing it.
- **Uploading:** a student sends a photo or an assignment.
- Express can send files by itself with `res.sendFile` and `res.download`.
- Uploads need a package. The usual one is **Multer**.

**Simple lesson:** Express sends files. Multer receives them.

### A Real-World Example

The school office handles papers all day.

1. A parent asks for the fee receipt. The clerk hands over a copy.
2. A student submits an assignment. The clerk checks it is the right subject and not too thick.
3. Accepted papers go into a locked cupboard, under a number, not the student's own label.
4. Nobody may walk in and pick any file from the cupboard.

Handing over is sending. Checking and storing is uploading. The locked cupboard is file security.

In this lesson, you will learn:

1. How to send files with `res.sendFile`.
2. How to offer downloads with `res.download` and `res.attachment`.
3. Why large files should be streamed.
4. How to accept uploads with Multer.
5. How to check uploaded files.
6. How to keep files and folders safe.

## Sending Files with res.sendFile

- **res.sendFile(path)** sends a file and sets its `Content-Type`.
- The path must be **absolute**, such as `path.join(import.meta.dirname, "files", "menu.pdf")`.
- Or give a relative name plus the **root** option: `res.sendFile("menu.pdf", { root: dir })`.
- The **root** option also blocks `..` tricks, so it is the safer choice.
- A missing file is an error. Handle it in a callback or let error middleware do it.

- Each file gets the right `Content-Type`. Check **Headers**.
- `/missing` uses the callback to send a friendly 404.

**Simple lesson:** Use `res.sendFile` with the `root` option.

## Offering Downloads

- **res.download(path, name)** = send the file as a download.
- The browser saves it instead of showing it.
- The second argument is the file name the user sees.
- It sets the **Content-Disposition** header: `attachment; filename="..."`.
- **res.attachment(name)** sets only that header. Use it when you send the body yourself.

- Open **Headers** and find `Content-Disposition` in both answers.

**Simple lesson:** `res.download` sends a file to be saved, with a friendly name.

## Large Files and Streaming

- `fs.readFileSync` loads the whole file into memory first.
- A 2 GB video read this way could crash the server.
- **Streaming** = sending the file in small pieces as it is read.
- `fs.createReadStream(path).pipe(res)` streams a file.
- **Backpressure** = the stream pauses when the client reads slowly, so memory stays low.
- `res.sendFile` and `res.download` already stream for you.

Always listen for `error` on a stream. An unhandled stream error stops the whole Node.js process.

**Simple lesson:** Stream big files. Never load them fully into memory.

## Uploading Files

An HTML form that uploads files must use `enctype="multipart/form-data"`.

```html
<form action="/photo" method="post" enctype="multipart/form-data">
  <input type="text" name="name">
  <input type="file" name="photo">
  <button>Upload</button>
</form>
```

- **multipart/form-data** = a body split into parts, one per field.
- **File fields** carry a file name, a type and the bytes.
- **Text fields** carry plain values, like `name`.
- `express.json()` and `express.urlencoded()` cannot read this format.
- API clients, like Postman or `curl -F`, send the same format.

### Multer

- Install: `npm install multer`.
- Create an upload handler: `const upload = multer({ ... })`.
- **upload.single("photo")** = one file. It lands in **req.file**.
- **upload.array("photos", 3)** = up to 3 files with one name. They land in **req.files**.
- **upload.fields([...])** = files under several names. `req.files` becomes an object.
- Text fields from the same form land in **req.body**.

- The quick requests send real multipart bodies, like a browser form.

**Simple lesson:** `upload.single` for one file, `upload.array` for many, both before your handler.

## Where Uploads Are Stored

- **Memory storage** (the default) = the file stays in `req.file.buffer`. Good for small files you pass on, such as to cloud storage.
- **Disk storage** = Multer writes the file to a folder.
- `multer({ dest: "uploads/" })` saves with a random name and no extension.
- `multer.diskStorage({ destination, filename })` lets you choose the folder and the name.
- **Custom storage engines** send files straight to places like Amazon S3.
- **Temporary files** should be deleted when you are done with them.

- The saved name is a random UUID plus `.pdf`. The user's odd name is never used as a path.

**Simple lesson:** Store uploads under names you generate, in a folder you choose.

## Checking Uploaded Files

Treat every upload as possibly wrong or harmful.

- **File size limit:** `limits: { fileSize: 2 * 1024 * 1024 }` for 2 MB.
- **File count limit:** `limits: { files: 3 }`.
- **Allowed extensions:** an allowlist, such as `.pdf`, `.png`, `.jpg`.
- **Declared MIME type:** `file.mimetype`. The client chooses it, so it can lie.
- **File signature:** the first bytes of the file, such as `%PDF` for PDFs. Harder to fake.
- **fileFilter** = a Multer option that accepts or rejects each file.

- `virus.exe` fails the extension check, even though it claims to be a PDF.
- `fake.pdf` passes the name check, but fails the signature check.
- `big.pdf` is stopped by the size limit with 413.

Checking the first bytes is a good extra check, but it is not perfect. Real apps may also scan files for viruses, and never run or open uploaded files on the server.

**Simple lesson:** Check size, extension, type and signature. Trust none of them alone.

## File Security

- **Path traversal** = using `../` in a name to reach files outside a folder.
- Never build a path from user input with plain `path.join`.
- Use the `root` option, or `path.basename(name)` to strip folders.
- Never allow uploads that can run, like `.exe`, `.sh` or `.html`.
- Keep **private uploads outside** `public/`. Send them through a route that checks login.
- Use generated names, and delete temporary files when done.

- `/unsafe?file=../server.js` sends your source code and the password.
- `/safe?file=../server.js` is refused with 403.

Path traversal is one of the most common file bugs. Every file name from a user is an attack until proven safe.

**Simple lesson:** User input never decides a full file path.

## Handling Upload Errors

- Multer passes problems to `next(err)` as a **MulterError**.
- `err.code` says what went wrong.
- **LIMIT_FILE_SIZE:** the file is too big. Answer 413.
- **LIMIT_FILE_COUNT:** too many files. Answer 400.
- **LIMIT_UNEXPECTED_FILE:** a file arrived under the wrong field name. Answer 400.
- **Missing file:** `req.file` is `undefined`. Check it in your handler.
- Answer every upload error with the same JSON shape.

**Simple lesson:** Turn Multer errors into clear, consistent JSON answers.

## Common mistakes

<div className="tscroll">

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| `res.sendFile("menu.pdf")` | Relative paths work | Use an absolute path or `root` | `TypeError: path must be absolute` |
| `path.join(dir, req.query.file)` | `path.join` is safe | It follows `../` | Source code and secrets leak |
| Form without `enctype="multipart/form-data"` | Files upload with any form | Uploads need multipart | `req.file` is `undefined` |
| Trusting `file.mimetype` | The browser checks types | The client sets it freely | Harmful files get in |
| Saving with `file.originalname` | Users pick sensible names | Names can hold `../` or clash | Overwrites and traversal |
| Uploads inside `public/` | It is easy to serve them | Private files need a login check | Anyone can download anyone's files |
| `readFileSync` for big downloads | Files are small | Stream big files | High memory, crashes |

</div>

## Check Yourself

## Exercises

## What to Remember

- `res.sendFile` with the `root` option sends files safely.
- `res.download` and `res.attachment` make the browser save the file.
- Stream large files, and always handle stream errors.
- Multer reads uploads: `single` to `req.file`, `array` and `fields` to `req.files`.
- Check size, extension, MIME type and signature. Use generated file names.
- Never build paths from user input, and keep private files out of `public/`.

Next: **Working with a Database**. You will store canteen data in SQLite and query it safely.
