Telerik blogs
How ToT2 Dark_1200x303

In this article, we will go through the steps involved in uploading images to the cloud using Node.js, and we will be using Cloudinary as our cloud storage.

This article will go through the steps involved in uploading images to cloud storage with Node.js, and the cloud storage service we will be using is Cloudinary. When it comes to creating and delivering your digital assets with the least amount of effort, Cloudinary has you covered with an end-to-end management solution.

This post assumes that you are familiar with the basics of JavaScript and Node.js.

Prerequisites

To follow along, I recommend you have the following:

  • Node version 10 or newer
  • A basic understanding of JavaScript and Node.js
  • An Integrated Development Environment (e.g., Visual Studio Code)

Set Up Your Project

Let’s set up our development environment. Open your terminal and run the following command.

mkdir proEnv
cd proEnv
yarn init

You’ll be asked a few questions, and your answers will be used to generate your package.json file.

Next, we need to install Express—a framework for Node.js.

yarn add express

Let’s create a file named app.js at the root of our project that will serve as an entry point. Navigate to your parent folder and run the command below.

touch app.js

Now, add the following code inside the app.js file:

const express = require("express")
const app = express()
const port = 9000

app.use(express.json())

app.listen(port, () => {
  console.log(`Server is running on port:${port}`)
})

Run the following command in your terminal to start your server:

node app.js

You should see a message in your terminal saying, “Server is running on port 9000.” To automate our server modifications, track changes and restart the server whenever a change occurs, we need to install nodemon as a dev dependency.

yarn add -D nodemon

For the package to work as expected, let’s add a script to our package.json file.

"scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
}

Your package.json file should look like this:

{
  "name": "image-upload-api",
  "version": "1.0.0",
  "description": "Uploading Images to Cloudinary",
  "main": "app.js",
  "author": "Sproff",
  "license": "MIT",
  "scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}

Error Handler

Create a folder at the root of your project called utils. Inside the folder, create a file named errorHandler.js and add the following to it:

class ErrorHandler extends Error {
  constructor(statusCode, message) {
    super();
    this.statusCode = statusCode;
    this.message = message;
  }
}

module.exports = {
  ErrorHandler,
};

We created a global error handler to avoid duplicating or rewriting functions that’ll handle errors that may arise while uploading an image or failing to follow a required step. It assigns a status Code and a message depending on the request.

Configure a .env File

Require dotenv in your app.js file by adding the following:

require('dotenv').config()

Create a file at the root of your project named .env. Go to your Cloudinary dashboard, and copy and paste your Cloud name, API Key and API Secret.

CLOUDINARY_CLOUD_NAME=xxx
CLOUDINARY_API_KEY=xxx
CLOUDINARY_API_SECRET=xxx

Image Upload Function

At the root of your project, create a folder called controller. Inside the folder, create a file named upload.controller.js and add the following code to it:

const { ErrorHandler } = require('../utils/errorHandler')

const uploadImage = async (req, res, next) => {
  try {
      res.json({
      status: 'success',
      message: 'Upload successful',
    })
  } catch (error) {
    next(new ErrorHandler(error.statusCode || 500, error.message))
  }
}

module.exports = {
  uploadImage,
}

We passed three parameters to the uploadImage function. It returns the result of a successful image upload and handles errors with the error handler we created earlier.

Next, we need to install the following packages. Open your terminal and run the following command:

yarn add multer cloudinary dotenv

The command above will install Multer, which is middleware used to handle requests with multipart/form-data header and Cloudinary. Both packages will help us upload our image effectively. We also installed dotenv, which is an npm package we’ll use to keep our configurations private.

At the root of your project, create a folder named service. Inside the folder, create a file named upload.service.js and add the following to it:

const multer = require("multer");
const cloudinary = require("cloudinary");
const { ErrorHandler } = require("../utils/errorHandler");

cloudinary.config({
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
  api_key: process.env.CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET,
});

const memoryStorage = multer.memoryStorage();

const upload = multer({
  storage: memoryStorage,
});

const uploadToCloudinary = async (fileString, format) => {
  try {
    const { uploader } = cloudinary;

    const res = await uploader.upload(
      `data:image/${format};base64,${fileString}`
    );

    return res;
  } catch (error) {
    throw new ErrorHandler(500, error);
  }
};

module.exports = {
  upload,
  uploadToCloudinary,
};

Multer allows us to effortlessly transfer files from a browser to the server we desire. Without Multer, we won’t be able to access any data because the request body will be empty.

In the code above, we configure Cloudinary with our credentials stored in the .env file. The process.env property will have access to the keys defined there. We create a function called uploadToCloudinary that accepts the buffer that needs to be processed before uploading the image to Cloudinary. The upload function serves as middleware that will be called whenever you want to run Multer, and it returns basic information such as the type of storage. In our case, we’re using MemoryStorage.

Multer allows us to store files dynamically in two ways—DiskStorage and MemoryStorage. We’re using MemoryStorage because it is the most common kind of storage, and it allows us to communicate with our server while keeping the buffer in memory for future use.

Next, we need to install a package that will allow us to convert between formats—in our case, buffer to data URI. Open your terminal and run the following command:

yarn add datauri

Inside your utils folder, create a file named file.js and add the following to it:

const DatauriParser = require('datauri/parser')

const parser = new DatauriParser()

const bufferToDataURI = (fileFormat, buffer) =>
  parser.format(fileFormat, buffer)

module.exports = {
  bufferToDataURI,
}

The DatauriParser function here acts as a file format conversion passage. We have a file called buffer that needs to be converted to data URI before it can be uploaded to Cloudinary because Cloudinary doesn’t know what buffer is. The parser will look for the file format .png or .jpg, and convert the buffer to a string.

Now replace the code you have in your upload.controller.js file with this updated one:

const { uploadToCloudinary } = require("../service/upload.service");
const { ErrorHandler } = require('../utils/errorHandler')
const { bufferToDataURI } = require('../utils/file')

const uploadImage = async (req, res, next) => {
  try {
    const { file } = req
    if (!file) throw new ErrorHandler(400, 'Image is required')

    const fileFormat = file.mimetype.split('/')[1]
    const { base64 } = bufferToDataURI(fileFormat, file.buffer)

    const imageDetails = await uploadToCloudinary(base64, fileFormat)

    res.json({
      status: 'success',
      message: 'Upload successful',
      data: imageDetails,
    })
  } catch (error) {
    next(new ErrorHandler(error.statusCode || 500, error.message))
  }
}

module.exports = {
  uploadImage,
}

We’re importing the bufferToDataURI function in the code above and selecting the file format we want to accept. We destructured the file name from the request body and passed it through a simple check to avoid errors. We get the fileFormat from the file object, split it to select a specific type, and pass the buffer to the bufferToDataURI function, which returns a base64 string.

uploadToCloudinary function here accepts the fileFormat and the base64 string. After a successful upload, res provides a response with all of the details of the image.

Create the Upload Route

Let’s create our route. At the root of your project, create a folder named routes, and inside it, create a file named upload.routes.js. Add the following to the file:

const { Router } = require('express')
const { uploadImage } = require('../controller/upload.controller')
const { upload } = require('../service/upload.service')

const router = Router()

router.post('/', upload.single('image'), uploadImage)

module.exports = router

Here we destructured the Express router and assigned it to a variable to generate a post request. We’re using the Multer middleware, and upload.single simply uploads a single image. There are other methods for uploading images, such as upload.array, which can upload multiple images.

Now, let’s import our newly created route into our app.js file.

const express = require("express");
require("dotenv").config();
const app = express();
const uploadRouter = require('./routes/upload.routes')
const port = process.env.PORT || 9000;

app.use(express.json());
app.use('/upload', uploadRouter)

app.listen(port, () => {
  console.log(`Server running on port: ${port}`);
});

Screenshot of Cloudinary Media Library

To view the image you just uploaded, log in to your Cloudinary account and check your media library.

Conclusion

This post covered the steps involved in uploading images directly to Cloudinary using Express.js and Multer. We saw how easy it was to integrate Cloudinary with these third-party libraries.


Ifeoma-Imoh
About the Author

Ifeoma Imoh

Ifeoma Imoh is a software developer and technical writer who is in love with all things JavaScript. Find her on Twitter or YouTube.

Related Posts

Comments

Comments are disabled in preview mode.