Upload images to AWS S3 using multer in node.js
Tags: node, aws, javascript, tutorial
Published: 2022-07-13 09:40:51

Multer is a node.js middleware and it handles multipart/form-data that includes files, images etc. 

It mostly occurs in projects that we need to handle file uploads in our APIs. We can save those files in local disk storage on our system, but its better to have a hosted platform where we can upload our files and access through anywhere using the link. One such cloud storage is AWS S3. 

In this post, we will be uploading images to S3 storage using multer. Likewise, you can handle any kind of file you like. Also, we will learn to use the `version 3` of AWS sdk, which has a little different setup than the previous version.

## Prerequisites

1. Basics of node.js and express 

## Setup AWS Account

1. Create an account on AWS. 
2. There should be `services` tab/link. From there, we can select `S3`. It will be empty but we will be able to see objects in it once we start uploading files in it.
3. Grab your access keys from your files in credentials section. You should have `access key id` and `secret key`. We will use these when configuring multer.
4. Choose your AWS **region** as per your location and take a note of its region id. 

## Setup Multer

To connect with AWS S3, we need its SDK and multer-s3 package. We will be using AWS SDK V3.

**Install Multer, multer-s3 and aws sdk**

```bash
$ npm install --save multer multer-s3 @aws-sdk/client-s3
```

**Create multer middleware**

In your middlewares folder, create a file `uploadImage.js`. You can name it anything you like. 

**Step 1: In `uploadImage.js`, we will setup the S3 client:**

```javascript
const multer = require("multer");
const multerS3 = require("multer-s3");
const { S3Client } = require("@aws-sdk/client-s3");

// create s3 instance using S3Client 
// (this is how we create s3 instance in v3)
const s3 = new S3Client({
    credentials: {
        accessKeyId: "YOUR_ACCESS_KEY_ID_HERE", // store it in .env file to keep it safe
        secretAccessKey: "YOUR_SECRET_KEY_HERE"
    },
    region: "ap-south-1" // this is the region that you select in AWS account
})
```



**Step 2: In `uploadImage.js`, add the following. use multer-s3 to connect to client and create s3 storage for multer**

```javascript
const s3Storage = multerS3({
    s3: s3, // s3 instance
    bucket: "my-images", // change it as per your project requirement
    acl: "public-read", // storage access type
    metadata: (req, file, cb) => {
        cb(null, {fieldname: file.fieldname})
    },
    key: (req, file, cb) => {
        const fileName = Date.now() + "_" + file.fieldname + "_" + file.originalname;
        cb(null, fileName);
    }
});
```



**Step 3: Using our storage instance to create our `uploadImage` middleware using multer**

```javascript
// function to sanitize files and send error for unsupported files
function sanitizeFile(file, cb) {
	// Define the allowed extension
	const fileExts = [".png", ".jpg", ".jpeg", ".gif"];
	
    // Check allowed extensions
	const isAllowedExt = fileExts.includes(
		path.extname(file.originalname.toLowerCase())
	);

	// Mime type must be an image
	const isAllowedMimeType = file.mimetype.startsWith("image/");
	
    if (isAllowedExt && isAllowedMimeType) {
		return cb(null, true); // no errors
	} else {
		// pass error msg to callback, which can be displaye in frontend
		cb("Error: File type not allowed!");
	}
}

// our middleware
const uploadImage = multer({
    storage: s3Storage,
    fileFilter: (req, file, callback) => {
        sanitizeFile(file, callback)
    },
    limits: {
        fileSize: 1024 * 1024 * 2 // 2mb file size
    }
})

module.exports = uploadImage;
```



## Using uploadImage middleware in routes

Lets create a route where a user can update his profile image. I assume express and router is already setup.

```javascript
// update profile image
router.put(
    "/:userId/profile-image", 
    uploadImage.single("image"), // our uploadImage middleware
    (req, res, next) => {
        /* 
           req.file = { 
        	 fieldname, originalname, 
        	 mimetype, size, bucket, key, location
           }
        */
        
        // location key in req.file holds the s3 url for the image
        let data = {}
        if(req.file) {
            data.image = req.file.location
        }
        
        // HERE IS YOUR LOGIC TO UPDATE THE DATA IN DATABASE
    }
)
```



So, this was it for using multer with AWS and v3 SDK. I hope you find it helpful. 

:v: