Cloudinary Image Upload in Node.js: Complete Guide
Cloudinary is a powerful cloud-based image and video management service. In this guide, we'll learn how to integrate Cloudinary with Node.js and Express.js for handling product image uploads, optimizations, and gallery management.
Cloudinary is a powerful cloud-based image and video management service. In this guide, we'll learn how to integrate Cloudinary with Node.js and Express.js for handling product image uploads, optimizations, and gallery management.
Installation
npm install cloudinaryCloudinary Configuration
Setting up Cloudinary configuration:
const cloudinary = require("cloudinary").v2;
const fs = require("fs");
const path = require("path");
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});Upload Single Image
Creating a function to upload a single product image:
async function uploadProductImageToCloudinary(filename) {
try {
const file = `./uploads/${filename}`;
const uploadedImageResult = await cloudinary.uploader.upload(file);
// Generate optimized image URL
const optimizedImageUrl = cloudinary.url(uploadedImageResult.public_id, {
transformation: [
{
quality: "auto",
fetch_format: "auto",
},
{
width: 400,
height: 400,
crop: "fill",
gravity: "auto",
},
],
});
const imageData = {
optimizedUrl: optimizedImageUrl || "",
secureUrl: uploadedImageResult?.secure_url || "",
publicId: uploadedImageResult?.public_id || "",
url: uploadedImageResult?.url || "",
};
// Delete local file after upload
try {
fs.unlinkSync(path.join(__dirname, "..", "uploads", filename));
} catch (error) {
console.log("Error deleting local image", error);
}
return imageData;
} catch (error) {
console.log("Error uploading image to Cloudinary", error);
return null;
}
}Upload Gallery Images
Uploading multiple gallery images:
async function uploadProductGalleryToCloudinary(filenames) {
if (!filenames || filenames.length === 0) {
return [];
}
const galleryImages = [];
for (const filename of filenames) {
try {
const file = `./uploads/${filename}`;
const uploadedImageResult = await cloudinary.uploader.upload(file);
const optimizedImageUrl = cloudinary.url(uploadedImageResult.public_id, {
transformation: [
{ quality: "auto", fetch_format: "auto" },
{ width: 800, height: 800, crop: "fill", gravity: "auto" },
],
});
galleryImages.push({
optimizedUrl: optimizedImageUrl || "",
secureUrl: uploadedImageResult?.secure_url || "",
publicId: uploadedImageResult?.public_id || "",
url: uploadedImageResult?.url || "",
});
// Delete local file
try {
fs.unlinkSync(path.join(__dirname, "..", "uploads", filename));
} catch (error) {
console.log("Error deleting local gallery image", error);
}
} catch (error) {
console.log(`Error uploading gallery image ${filename}:`, error);
}
}
return galleryImages;
}Delete Images
Deleting images from Cloudinary:
async function deleteImageFromCloudinary(publicId) {
try {
const deletedImage = await cloudinary.uploader.destroy(publicId, {
resource_type: "image",
});
console.log("Deleted image from Cloudinary:", deletedImage);
return true;
} catch (error) {
console.log("Error deleting image from Cloudinary", error);
return false;
}
}
// Delete multiple images
async function deleteMultipleImagesFromCloudinary(publicIds) {
if (!publicIds || publicIds.length === 0) {
return true;
}
try {
const results = await cloudinary.api.delete_resources(publicIds, {
resource_type: "image",
});
console.log("Deleted images from Cloudinary:", results);
return true;
} catch (error) {
console.log("Error deleting multiple images from Cloudinary", error);
return false;
}
}Using in Product Controller
Integrating Cloudinary upload in product creation:
const {
uploadProductImageToCloudinary,
uploadProductGalleryToCloudinary,
} = require("../utils/cloudinaryHelper");
async create(req, res) {
try {
const newProduct = await productModel.create(productData);
// Upload main product image to Cloudinary
if (req.files.product_image && req.files.product_image.length > 0) {
const mainImageData = await uploadProductImageToCloudinary(
req.files.product_image[0].filename
);
if (mainImageData) {
await productModel.update(newProduct.id, {
product_image_optimizedUrl: mainImageData.optimizedUrl,
product_image_secureUrl: mainImageData.secureUrl,
product_image_publicId: mainImageData.publicId,
});
}
}
// Upload gallery images
if (req.files.product_gallery && req.files.product_gallery.length > 0) {
const galleryFilenames = req.files.product_gallery.map((file) => file.filename);
const galleryImages = await uploadProductGalleryToCloudinary(galleryFilenames);
if (galleryImages.length > 0) {
await productModel.update(newProduct.id, {
product_gallery: galleryImages,
});
}
}
const updatedProduct = await productModel.getById(newProduct.id);
return res.status(201).json(updatedProduct);
} catch (error) {
console.error("Error creating product:", error);
return res.status(500).json({
message: "Error creating product",
error: error.message,
});
}
}Environment Variables
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secretBest Practices
- Always delete local files after uploading to Cloudinary
- Use optimized URLs for better performance
- Store public_id for easy deletion later
- Implement error handling for upload failures
- Use transformations for consistent image sizing
- Clean up old images when updating products
Conclusion
Cloudinary provides a robust solution for image management in Node.js applications. With automatic optimizations, transformations, and CDN delivery, it's perfect for product image management in inventory systems. The integration is straightforward and provides excellent performance for image-heavy applications.