How to Use S3 File Storage with Node.js and AWS SDK for Efficient File Management

Learn how to store and manage files in AWS S3 using Node.js with the AWS SDK client.

NodejsFeb 27, 2024Nagesh
How to Use S3 File Storage with Node.js and AWS SDK for Efficient File Management

Dependencies

Json
  "dependencies": {
    "@aws-sdk/client-s3": "^3.382.0",
    "aws-sdk": "^2.1427.0",
    "multer": "^1.4.5-lts.1",
    "multer-s3": "^3.0.1",
  },

Config

Javascript
{
  AWS_BUCKET_NAME: process.env.AWS_BUCKET_NAME,
  s3Config: {
      credentials: {
          accessKeyId: process.env.S3_BUCKET_ACCESS_KEY_ID,
          secretAccessKey: process.env.S3_BUCKET_SECRET_ACCESS_KEY,
      },
      region: 'ap-south-1',
      logging: true
  }
}

Creating S3 Storage

Javascript
const { S3Client } = require('@aws-sdk/client-s3')
const multerS3 = require('multer-s3');
const { AWS_BUCKET_NAME, s3Config } = require('../../config');

// Configure your AWS credentials and region
const s3 = new S3Client(s3Config);

const s3Storage = (subDirectory) => {
 return multerS3({
  s3: s3,
  bucket: AWS_BUCKET_NAME,
  acl: 'public-read', // Set the ACL permissions for the uploaded file
  key: function (req, file, cb) {
    const filepath = `user/${req.user._id}/${subDirectory}/`;
    const filename = Date.now().toString() + '-' + file.originalname;
    const fullpath = filepath + filename;
    cb(null, fullpath);
  },
});
}
module.exports = s3Storage;

Creating Multer middleware Using S3 storage

Javascript
const multer = require('multer');
const s3Storage = require('../utils/multer/s3Storage');
const { acceptImageFileType } = require('../utils/multer/acceptImageFileType');

const uploadImage = (subDirectory) => {
  return multer({
    storage: s3Storage(subDirectory),
    limits: {
      fileSize: 3000000 // bytes same as 3MB
    },
    fileFilter: function (req, file, cb) {
      acceptImageFileType(file, cb);
    }
  });
} 

module.exports = uploadImage;

Optional Filter file type

Javascript
const path = require('path');

function acceptImageFileType(file, cb) {
  // Allowed filetypes
  const filetypes = /jpeg|jpg|png/;

  // Check extension
  const extname = filetypes.test(
    path.extname(file.originalname).toLowerCase()
  );

  // Check mimetype
  const mimetype = filetypes.test(file.mimetype);

  if (mimetype && extname) {
    return cb(null, true);
  } else {
    cb('Error: Images Only!');
  }
}
exports.acceptImageFileType = acceptImageFileType;

using using image upload middleware on route

Javascript
router.post(
  "/user-profile-cover",
  uploadUserImage('profileCover').single("file"),
  updateProfileCover
);

Handling image link on controller

Javascript
module.exports.updateProfileCover = async (req, res) => {
  try {
    // Check if file is present in the request
    if (!req.file) {
      return error({ res, msg: strings.pleaseUploadImage });
    }

    const user = await User.findById(req.user._id);

    if (!user) {
      return error({ res, msg: strings.userNotFound, status: 404 });
    }

    // Delete Existing profile picture
    deleteFileFromBucket(user.profileCover);

    // Save the file path to the user document
    user.profileCover = req.file.location;

    // Save the updated user document
    await user.save();
    success({
      res,
      msg: strings.successUploadProfileCover,
      data: {
        profileCover: user.profileCover,
      },
    });
  } catch (err) {
    console.error(err);
    error({ res, msg: strings.SERVER_ERR });
  }
};

Tags