How to Upload Audio and Image Files to Cloudinary with Node.js and Typescript

Introduction

I've worked on a project where I needed to upload both an image and audio file to Cloudinary and it was initially confusing for me. In this guide, I'm going to simplify the process, I'll walk you through the step-by-step processes you need to successfully put your multimedia data on the cloud.

Installations

npm install express multer cloudinary

Multer is the middleware you'll be using for handling file uploads.

Step-by-Step Guide

  1. Setup Cloudinary

    First, ensure you have signed up for Cloudinary and obtain your API credentials (cloud name, API key, and API secret). After that, configure Cloudinary in your Typescript application (most likely in your index file):

import "dotenv/config"
import { v2 as cloudinary } from 'cloudinary'

cloudinary.config({
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
  api_key: process.env.CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET,
})
  1. Handling File Uploads with Multer

    Integrate Multer with memory storage into your application. This approach avoids unnecessary disk storage, enhancing performance and scalability. Here's the setup (in the upload route):

import express, { Request, Response } from 'express'
import multer from 'multer'
import { v2 as cloudinary } from 'cloudinary'

const router = express.Router()

const storage = multer.memoryStorage()
const upload = multer({ storage })

router.post(
  '/upload',
  upload.fields([
    { name: 'image', maxCount: 1 },
    { name: 'audio', maxCount: 1 },
  ]),
  async (req: Request, res: Response) => {
    if (!req.files) {
      return res.status(400).json({ message: 'No files uploaded' })
    }
    const files = req.files as { [fieldname: string]: Express.Multer.File[] }
    const imageFile = files['image'][0]
    const audioFile = files['audio'][0]

    try {
      //upload image
      const imageResult = await new Promise(
        (resolve, reject) => {
          const stream = cloudinary.v2.uploader.upload_stream(
            { resource_type: 'image' },
            (error, result) => {
              if (error) reject(error)
              resolve({
                url: result?.secure_url,
                id: result?.public_id,
              })
            }
          )
          stream.end(imageFile.buffer)
        }
      )
      //upload audio
      const audioResult = await new Promise(
        (resolve, reject) => {
          const stream = cloudinary.v2.uploader.upload_stream(
            { resource_type: 'video' },
            (error, result) => {
              if (error) reject(error)
              resolve({
                url: result?.secure_url,
                id: result?.public_id,
              })
            }
          )
          stream.end(audioFile.buffer)
        }
      )

      return res.status(200).json({imageResult, audioResult})
    } catch (error) {
      console.log(error)
      res.status(500).json({ message: 'Error uploading files' })
    }
  }

Detailed explanation of the upload (for image):

  • Creating a Promise:

    • The code wraps the upload process inside a JavaScript Promise. This allows the use of async/await for handling the asynchronous operation of uploading the file.

    • new Promise((resolve, reject) => { ... }) initializes the promise. The resolve and reject functions are used to complete the promise successfully or handle errors, respectively.

  • Setting Up Cloudinary Upload Stream:

    • cloudinary.v2.uploader.upload_stream is a function provided by the Cloudinary SDK that returns a writable stream. This stream is used to upload data to Cloudinary.

    • The function takes two parameters:

      • An options object: { resource_type: 'image' } specifies that the upload is an image. Other types can include 'video' or 'raw' for different media or file types.

      • A callback function: (error, result) => { ... } which is called once the upload is complete or if an error occurs.

  • Handling the Upload Result:

    • Inside the callback function, error is checked first. If an error occurs during the upload, reject(error) is called to reject the promise, allowing it to be caught in a try-catch block.

    • If the upload is successful, the result contains the information about the uploaded image. This typically includes a secure_url for accessing the image and a public_id that uniquely identifies the image in Cloudinary.

    • The result is then processed to extract the url (result?.secure_url) and id (result?.public_id), which are used to create an object { url, id } and resolve the promise with this object.

  • Ending the Stream:

    • stream.end(imageFile.buffer); ends the writable stream and sends the imageFile buffer to Cloudinary. imageFile.buffer contains the binary data of the image file being uploaded.

The same process goes for the audio file.

Expected result:

{
    {
      url: "https://res.cloudinary.com/your-cloud-name/image/upload/v1628390000/sample.jpg",
      id: "sample"
    },
    {
      url: "https://res.cloudinary.com/your-cloud-name/audio/upload/v1628390000/sample.jpg",
      id: "sample"
    }
}

How It All Works Together

  • File Buffer: The imageFile.buffer is the raw binary data of the file that is read into memory. This is passed to the Cloudinary upload stream.

  • Stream: The Cloudinary stream uploads the binary data to Cloudinary’s servers.

  • Callback: The callback receives the result (or error) of the upload process.

  • Promise Handling: If the upload is successful, the promise is resolved with the URL and ID of the uploaded image. If there's an error, the promise is rejected with the error.

  1. Integrate with Your Web App

Create a frontend with enctype="multipart/form-data to send audio and image files to your endpoint.

Conclusion

In this guide you learned how to upload an image and audio file to Cloudinary. You configured cloudinary in your application and set up the upload stream for the image and audio file. You learned how the upload process works and how to integrate with your frontend.

Hope this guide helps you!

1
Subscribe to my newsletter

Read articles from Hosea Tonzai Favour directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hosea Tonzai Favour
Hosea Tonzai Favour