---
title: "Handling file uploads and failures with Express"
date: 2022-10-25T00:00:00
slug: handling-file-uploads-and-failures-with-express

---
Another little snippet to remind myself later.

If I want to handle uploading files via a form with `enctype="multipart/form-data"`, I need to include a third-party library. In this case, [Multer](https://npmjs.org/package/multer) is the best choice.

The usage is pretty simple. At it’s most basic:

```typescript
import express from 'express';
import multer from 'multer';
import path from 'node:path';

const app = express();

const upload = multer({
	dest: path.join(".", "public", "uploads")
});

app.post('/upload', upload.array('fieldname'), (req, res, next) => {
	// Array of files are stored in req.files
});
```

## [Using it with Middleware](https://lewisdale.dev/post/handling-file-uploads-and-failures-with-express/#using-it-with-middleware)

Multer works well for what it does, but there’s one issue with the default usage: if you have middleware that relies on form data, multer has to run *before* that middleware, or the `multipart/form-data` will not have been parsed. This is an issue when dealing with CSRF, because Multer doesn’t do dry-runs - if it runs, the files get uploaded to the server.

### [Option 1: Remove files on error](https://lewisdale.dev/post/handling-file-uploads-and-failures-with-express/#option-1-remove-files-on-error)

The first approach is pretty simple: include middleware after both Multer and your middleware that relies on form data. Check the response status, and if it’s an error response (`4xx` or `5xx`), iterate over `req.files` and remove each file manually.

e.g.

```typescript
import fs from 'fs';

const removeFilesOnError = (req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
	if (res.statusCode >= 400) {
		if (req.files) {
			(req.files as Express.Multer.File[]).forEach(file => {
				fs.unlinkSync(file.path);
			});
		} else if(req.file) {
			fs.unlinkSync(req.file.path);
		}
	}
};

app.post('/upload', upload.array('fieldname'), myOtherMiddleware, removeFilesOnError, (req, res, next) => {
	//
});
```

### [Option 2: Use MemoryStorage, and manually store files](https://lewisdale.dev/post/handling-file-uploads-and-failures-with-express/#option-2-use-memorystorage-and-manually-store-files)

By default, Multer uses `DiskStorage`, which automatically stores the files on your server. There is an option to use `MemoryStorage`, which adds a Buffer to each file containing the file contents. We can use this to only then store our files on a successful request:


```typescript
const storage = multer.memoryStorage();
const upload = multer({
	storage,
});

const saveOnSuccess = (req: Express.Request, res: Express.Response, next: Express.NextFunction) => {

	if (res.statusCode < 400) {
		if (req.files) {
			(req.files as Express.Multer.File[]).map(file => {
				// req.container is a Dependency Injection container I've defined separately
				// Substitute this line with any random string generator
				const filename = req.container.get_random_string();

				const storagePath = path.join(".", "public", "uploads", filename);
				fs.createWriteStream(storagePath).write(file.buffer).close();
				file.path = storagePath;

				return file;
			});
		}
	}
};


app.post('/upload', upload.array('fieldname'), myOtherMiddleware, saveOnSuccess, (req, res, next) => {
	// req.files will contain an array of files where file.path is the saved location
});
```

Of the two approaches I’ve listed, I think this is the one I prefer. They’re both fairly easy to implement, but this one allows me a bit more control over when files get saved - there’s no risk of a later error leaving malicious files on my server. The only caveat is that a large number of files (or a few very large files) will cause the application to run out of memory.

A third option could be to allow uploads via DiskStorage, but move them into an isolated storage until it’s determined that the request was successful, at which point they can be published.

Finally, a note to myself for when I inevitably face this issue again in the future:

**Do not attempt to write your own Mutlipart Form middleware, it won’t work and you’ll lose at least a full day.**
