In the past, I had already written two articles related to this topic. One was how to upload images to Cloudinary and the other was how to compress images with Node.js.
Today I decided to combine the knowledge from those articles. That is, I decided to compress the images and upload them to Cloudinary.
In this way, they will only spend credits on the space of the images and not on their compression and transformation.
Despite explaining step by step what we are going to do today, I recommend you go read the articles I mentioned.
Now with the introduction done, let's code!
Let's code
First let's install the necessary dependencies:
npm install express multer cloudinary sharp
Now we need a basic API:
const express = express();
const app = express();
app.get("/", (req, res) => {
return res.json({ message: "Hello world 🇵🇹" });
});
const start = () => {
try {
app.listen(3333);
} catch (error) {
console.error(error);
process.exit();
}
};
start();
After that we will configure multer and use MemoryStorage
:
const express = express();
const multer = require("multer");
const app = express();
const storage = multer.memoryStorage();
const upload = multer({ storage });
app.get("/", (req, res) => {
return res.json({ message: "Hello world 🇵🇹" });
});
const start = () => {
try {
app.listen(3333);
} catch (error) {
console.error(error);
process.exit();
}
};
start();
Next, let's configure Cloudinary using its SDK:
const express = express();
const multer = require("multer");
const cloudinary = require("cloudinary").v2;
const app = express();
const storage = multer.memoryStorage();
const upload = multer({ storage });
cloudinary.config({
cloud_name: "YOUR_CLOUD_NAME",
api_key: "YOUR_API_KEY",
api_secret: "YOUR_API_SECRET",
});
app.get("/", (req, res) => {
return res.json({ message: "Hello world 🇵🇹" });
});
const start = () => {
try {
app.listen(3333);
} catch (error) {
console.error(error);
process.exit();
}
};
start();
From this point on, things will be different from other articles that have been written on this topic (written by me).
Regarding the image upload, we will send to Cloudinary the final result buffer we have after the transformations done with the sharp
module.
For that, let's create a function that will read the data from the buffer of the image that we'll pass as argument. And later the data will be returned as a buffer object.
The function we are going to create is as follows:
const { Readable } = require("stream");
// Hidden for simplicity
const bufferToStream = (buffer) => {
const readable = new Readable({
read() {
this.push(buffer);
this.push(null);
},
});
return readable;
}
Now we can proceed to the creation of the endpoint that we are going to use to upload the respective image.
Once created we will add the multer to our endpoint so we can have access to the image data. And we're going to name the field "picture".
app.post("/", upload.single("picture"), async (req, res) => {
// Logic goes here
});
Now with the endpoint created, let's start working on transforming the image.
In this example I will just convert the image to webp format and decrease its quality (to 20%). Then I'll get the final result as a buffer. In this way:
app.post("/", upload.single("picture"), async (req, res) => {
const data = await sharp(req.file.buffer).webp({ quality: 20 }).toBuffer();
// Even more logic goes here
});
Now we can start dealing with the configuration of sending our (buffered) image. So we will use the .upload_stream()
method (because we will be uploading a data stream). Then we'll define our destination folder (which I named "DEV"
).
And finally we will have a callback with two arguments, the first is the error and the second is the result. If an error occurs, we will log the error in the terminal. If we were successful, we will return a response with the image link.
Like this:
app.post("/", upload.single("picture"), async (req, res) => {
const data = await sharp(req.file.buffer).webp({ quality: 20 }).toBuffer();
const stream = cloudinary.uploader.upload_stream(
{ folder: "DEV" },
(error, result) => {
if (error) return console.error(error);
return res.json({ URL: result.secure_url });
}
);
// Almost done
});
We already have the transformed image buffer and the stream configuration that we are going to do. Now we just grab the image and send it to Cloudinary. For that we will use the .pipe()
method in our bufferToStream function.
That is, in our readable stream we will pass our transformed image buffer as the only argument. And in the pipe method we will pass our stream (destination) as the only argument.
Like this:
app.post("/", upload.single("picture"), async (req, res) => {
const data = await sharp(req.file.buffer).webp({ quality: 20 }).toBuffer();
const stream = cloudinary.uploader.upload_stream(
{ folder: "DEV" },
(error, result) => {
if (error) return console.error(error);
return res.json({ URL: result.secure_url });
}
);
bufferToStream(data).pipe(stream);
});
The final code should look like this:
const express = require("express");
const multer = require("multer");
const sharp = require("sharp");
const cloudinary = require("cloudinary").v2;
const { Readable } = require("stream");
const app = express();
const storage = multer.memoryStorage();
const upload = multer({ storage });
cloudinary.config({
cloud_name: "YOUR_CLOUD_NAME",
api_key: "YOUR_API_KEY",
api_secret: "YOUR_API_SECRET",
});
const bufferToStream = (buffer) => {
const readable = new Readable({
read() {
this.push(buffer);
this.push(null);
},
});
return readable;
};
app.get("/", (req, res) => {
return res.json({ message: "Hello world 🔥🇵🇹" });
});
app.post("/", upload.single("picture"), async (req, res) => {
const data = await sharp(req.file.buffer).webp({ quality: 20 }).toBuffer();
const stream = cloudinary.uploader.upload_stream(
{ folder: "DEV" },
(error, result) => {
if (error) return console.error(error);
return res.json({ URL: result.secure_url });
}
);
bufferToStream(data).pipe(stream);
});
const start = () => {
try {
app.listen(3333);
} catch (error) {
console.error(error);
process.exit();
}
};
start();
Have a great day!
I hope it helped you 👋
Top comments (8)
Thanks it was useful.👌
This is very useful. Do you have for s3 bucket?
Awesome it helps me greatly.
Awesome but how to upload multiple image?
how do i take the result and save it to database with mongoose?
I think the easiest way would be to take the URL returned by result.secure_url in the code below, then you can call an update on your appropriate model to add the URL.
const stream = cloudinary.uploader.upload_stream(
{ folder: "DEV" },
async (error, result) => {
if (error) return console.error(error);
await Model.findOneAndUpdate({filter}, {image: result.secure_url});
return res.json({ URL: result.secure_url });
}
);
Did you got the answer
Thank You for this amazing article 👏👏
I have uploaded img in png format in cloudanary but i want webp format img while fetching it in node js(strapi) so how can i achive that ?