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
- Basics of node.js and express
Setup AWS Account
- Create an account on AWS.
- There should be
services
tab/link. From there, we can selectS3
. It will be empty but we will be able to see objects in it once we start uploading files in it. - Grab your access keys from your files in credentials section. You should have
access key id
andsecret key
. We will use these when configuring multer. - 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
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
})
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);
}
});
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;
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
}
)
So, this was it for using multer with AWS and v3 SDK. I hope you find it helpful.
βοΈ
Top comments (4)
is there a way to show upload progress with this approach?
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.
uploadImage.single("image"), // our uploadImage middleware
^
TypeError: Cannot read property 'single' of undefined
getting this error
uploadImage was declared before, specifically this one