How to validate uploaded files in Node JS
Tags: node, javascript, webdev, multer
Published: 2022-03-26 12:18:17

In this note, we'll look at how we can handle file validation and compression in Node JS.
If you have a better way of handling validation or compression, please drop it in the comment section.
In most cases, files are parsed in a Node JS server using either Multer, busboy, or Formidable.
While the content used in this writeup uses Multer, it can easily apply to any system.

**File validation**
Files in Node JS are usually in JSON format. The format for files is one of the two shown below.

```json
// If memory storage is used
{
  fieldname: 'image',
  originalname: 'image.png',
  encoding: '7bit',
  mimetype: 'image/png',
  buffer: <Buffer bytes>,
  size: 25471
}

// If the file is stored locally
{
  fieldname: 'image',
  originalname: 'Meta1.png',
  encoding: '7bit',
  mimetype: 'image/png',
  destination: 'uploads/',
  filename: 'ed84692635f46d86c4be044f4acca667',
  path: 'uploads/ed84692635f46d86c4be044f4acca667',
  size: 25471
}
```
The fields we will use for validation are originalname, mimetype, and size fields.

## Checking the file extension.
We will use a bitwise right shift operator coupled with some inbuilt JS functions to get the file extension.
```javascript
const file_extension = image.originalname.slice(
    ((image.originalname.lastIndexOf('.') - 1) >>> 0) + 2
);
```
The above method has proven to work for 98% of cases, including misspelt filenames, i.e. image.png.png, photo.jpeg.jeg.

Since we now have the file extension, we can check to see if it's valid.

```javascript
// Array of allowed files
const array_of_allowed_files = ['png', 'jpeg', 'jpg', 'gif'];

// Get the extension of the uploaded file
const file_extension = image.originalname.slice(
    ((image.originalname.lastIndexOf('.') - 1) >>> 0) + 2
);

// Check if the uploaded file is allowed
if (!array_of_allowed_files.includes(file_extension)) {
  throw Error('Invalid file');
}
```

Checking only the file extension is not practical since anyone can edit a file name and change the extension, i.e. I can easily change a file name from `todo-list.docx` to `todo-list.png`.

For this reason, we will also need to check the mimetype of the file to ensure it's an image. We will follow a similar approach in doing this.

```javascript
const array_of_allowed_file_types = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];
if (!array_of_allowed_file_types.includes(image.memetype)) {
  throw Error('Invalid file');
}
```
combining the two checks, we'll have;

```javascript
// Array of allowed files
const array_of_allowed_files = ['png', 'jpeg', 'jpg', 'gif'];
const array_of_allowed_file_types = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];

// Get the extension of the uploaded file
const file_extension = image.originalname.slice(
    ((image.originalname.lastIndexOf('.') - 1) >>> 0) + 2
);

// Check if the uploaded file is allowed
if (!array_of_allowed_files.includes(file_extension) || !array_of_allowed_file_types.includes(image.memetype)) {
  throw Error('Invalid file');
}
```

## Checking file size
To check the file size, we use the size field. The size is usually given in bytes, so we have to convert it to the desired format for our evaluation. In our case, we converted it to MB.

```javascript
// Allowed file size in mb
const allowed_file_size = 2;
if ((image.size / (1024 * 1024)) > allowed_file_size) {					 
  throw Error('File too large');
}
```
Putting the above validations together, a typical middleware in express to validate uploaded files will look like the code below
```javascript
export const auth = (req, res, next) => {
    const image = req.file;
    // Array of allowed files
    const array_of_allowed_files = ['png', 'jpeg', 'jpg', 'gif'];
    const array_of_allowed_file_types = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];
    // Allowed file size in mb
    const allowed_file_size = 2;
    // Get the extension of the uploaded file
    const file_extension = image.originalname.slice(
        ((image.originalname.lastIndexOf('.') - 1) >>> 0) + 2
    );

    // Check if the uploaded file is allowed
    if (!array_of_allowed_files.includes(file_extension) || !array_of_allowed_file_types.includes(image.memetype)) {
        throw Error('Invalid file');
    }
    
    if ((image.size / (1024 * 1024)) > allowed_file_size) {					 
       throw Error('File too large');
    }
    return next();
}
```
## Conclusion
File validation is very important. Although this writeup made use of images, and a single file upload, it can easily be modified to work for other file types. Adding it inside a loop, it can validate an array of files as well.
The codes has been bundled up into an NPM package that can easily be integrated. follow the link to find it. [Fileguard](https://www.npmjs.com/package/@thesameeric/fileguard).