DEV Community

Cover image for Upload images to AWS S3 using multer in node.js
Paras 🧙‍♂️
Paras 🧙‍♂️

Posted on • Updated on

Upload images to AWS S3 using multer in node.js

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

$ npm install --save multer multer-s3 @aws-sdk/client-s3
Enter fullscreen mode Exit fullscreen mode

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:

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
})
Enter fullscreen mode Exit fullscreen mode

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

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);
    }
});
Enter fullscreen mode Exit fullscreen mode

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

// 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;
Enter fullscreen mode Exit fullscreen mode

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.

// 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
    }
)
Enter fullscreen mode Exit fullscreen mode

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

✌️

Top comments (4)

Collapse
 
vijaymeena profile image
vijay

is there a way to show upload progress with this approach?

Collapse
 
paras594 profile image
Paras 🧙‍♂️

I don't think there is a way. Never did this. Will let you know once I try it. Do share if you get the answer.

Collapse
 
sumitru53048845 profile image
sumit ruhela

uploadImage.single("image"), // our uploadImage middleware
^

TypeError: Cannot read property 'single' of undefined

getting this error

Collapse
 
rmrobb1e profile image
Robbie Mariano • Edited

uploadImage was declared before, specifically this one
Image description