Image Upload using Cloudinary

Having just finished up a coding bootcamp and trying to build some web apps myself, I realize how many of the features on the internet I take for granted. Take for example, the ability to upload an image. Before I started coding, I just clicked the button and didn’t think twice, kinda like how most people don’t think twice when they flush a toilet. It wasn’t until I had to implement an Image upload feature on one of my projects that I had to really understand the mechanisms behind how it worked.
Our project required a basic profile page, a profile page similar to the ones you see all the time on different websites. Basically a page that allows you to update information on your profile and also allow you to add a profile picture. My team and I had no idea how to do this, so we reached out to our awesome Instructor and he recommended we use a service known as Cloudinary to implement the image upload feature. He even provided us with a repo that goes over how to set up the code to get cloudinary to work in our app and also walked us through what each part of the code did. Thank you Anthony! None of the code was written by us. We just took what was provided by our instructor and implemented into our app. But in order to implement it, we had to understand how it worked and we couldn’t just copy and paste. The following next couple of paragraphs I am going to break down the code that was provided to us by our instructor, with the hope that you too will be able to implement this functionality in your app.
What is Cloudinary?
When a user uploads an image from the browser, that file needs to get stored somewhere and when the user requests for that file it needs to be sent back to them. But where do we store this file you ask? Cloudinary! Cloudinary allows us to store files uploaded by a user and then send it back to them if they request it, through easy to use REST api’s.
Getting Started
The first step in getting this up and running is to create a free account on Cloudinary. After you do that you will navigate to the section on your profile that contains your cloud name, KEY, and SECRET. You are going to need these three pieces of information so that node can interact with cloudinary. Once you have this, add these three pieces of info to your .env file. Alright! Another cool feature on your cloudinary account is the gallery. You can go to the gallery and see all of the images that have been uploaded from your app here.
Once you have that set up, you will need to install the following npm packages. Now I’ll assume your building out a full stack web app using MERN stack, there are packages you will need for your backend.
Backend Packages — Cloudinary, dotenv, and multer
Once you have downloaded all these npm packages you should be good to go! Let’s go ahead and set up our files.
Server.js
I call it server.js but you can call it whatever you want. This the file that you normally create to set up an express app. You will need to add the following to your file.
// NODEconst path = require('path');//This line of code will allow us to create temporary storage for the image that was uploaded by the user. It's temporary because it will stay here for a very short amount of time before being uploaded to cloudinary. const { existsSync, mkdirSync } = require('fs');// THIRD PARTYconst express = require('express');// We use this line to require all of the variables we specified inside of our dotenv file. This is important because we stored our cloudinary variables in there and were going to need that. require('dotenv').config();// Basic code for setting up express appconst PORT = process.env.PORT || 5100;const app = express();const routes = require('./routes');// Now the following 2 lines of code look similar to how we usually always set up our express app to deal with json data. The only key difference here is that we are limiting the size of that json data 10mb. Why? Because of the image uploads we are doing. It will prevent the user from uploading an image size greater that 10mb. app.use(express.urlencoded({ limit: '10mb', extended: true }));app.use(express.json({ limit: '10mb' }));if (process.env.NODE_ENV === "production") {app.use(express.static("client/build"));}//Routes
app.use(routes);// This code also looks familiar. It tells us what port our app is listening and and whether or not the connection to the server has been made. Key difference is that there is code that is creating a temporary folder (if it doesn't exist already) for storing files uploaded by the user. It uses the two methods imported from fs mentioned above. And as mentioned above, this is folder is temporary storage as the files will then get uploaded to cloudinary. app.listen(PORT, () => {console.log('APP running on… http://localhost:%s', PORT);var dir = path.join(__dirname, 'tmp/');if (!existsSync(dir)) mkdirSync(dir, 0744);});
Configuring Cloudinary
Alright, now that we have our express app file set up, we need to configure cloudinary. Create a cloudinary.js file. The following code will go into that file.
const cloudinary = require('cloudinary').v2;const options = {cloud_name: process.env.CLOUDINARY_NAME,api_key: process.env.CLOUDINARY_KEY,api_secret: process.env.CLOUDINARY_SECRET}cloudinary.config(options);module.exports = cloudinary;
From my understanding this connects our application to our cloudinary file using the information you stored in your .env file.
Creating Functionality for Dealing with Form Data and Uploading to Cloudinary
When the user uploads an image, they are basically submitting a form. Node unfortunately doesn’t know how to handle this data. Node usually is not good with parsing through data ( Ex. when we send JSON request, we usually always have to use middleware such as body parser to handle the data). Multer is the body parser equivalent for handling form data. It will intercept the data, convert it into a readable format for node, and then pass it on to the next necessary step in the process.
Create a file called upload.js. The following code should go into that file.
//we require multer from the npm package we installed
const multer = require('multer');//we require cloudinary from the cloudinary config file we created in the previous step.
const cloudinary = require('../config/cloudinary');//multer.diskStorage is storing the file to the temp folder you created.
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, './tmp/');
},
filename(req, file, cb) {
cb(null, file.fieldname + '-' +
Date.now().toString().toLowerCase());
}
});//this code tells multer to use the storage variable we created as its storage and .single('file') indicates the fieldname multer should go to when its looking for the file. We will then export this "upload" function so we can use it as middleware when we handle our routes.
const upload = multer({ storage }).single('file');
//Ok, so far we have set up code to recieve the image form data from the user and we are temporarily storing that data in our temp folder. We next need to create a function that will send that temp file to cloudinary. The following code does that and returns a promise. const uploadToCloudinary = (file, options) => {
return new Promise((resolve, reject) => {
cloudinary.uploader.upload(file, options, (error, result) => {
(result) ? resolve(result) : reject(error);
});
});};
module.exports = { upload, uploadToCloudinary };
Alright! Now we’re cooking. We have done the following so far. We set up our server.js file and added in code to create a temp directory (if one doesn’t exist) to store the data sent to us from the user. We the configured our cloudinary or in other words basically just connected our app to our cloudinary account. And lastly we set up another file that had logic for two important methods. The first method being our important “upload” middleware function that takes the response provided to us by the user and coverts into a format that node can read and stores it in our temp folder. And the second method being our uploadToCloudinary function that will take the file that is sitting in the temp folder and send it over to cloudinary. Last but not least, you need to set up a route that will handle all the image upload requests. You can use the following code to do this.
const path = require('path');const { unlinkSync } = require('fs');const router = require('express').Router();const { upload, uploadToCloudinary } = require('../controllers/upload');// ROUTESrouter.post('/api/upload', upload, async ({ file }, res) => {const result = await uploadToCloudinary(file.path, { folder: 'foo' });if (file) unlinkSync(file.path);res.json(result.public_id);});
To test and see if all this code works, you can use postman! It’s actually really cool because postman can allow you to upload an image and send that data to the server! To do this, click on the body tab, then select “form-data”. In the first section, switch the key to “image” and make the type a file. Once you do this, postman should allow you to upload an image. If all works out, after you upload an image through post man it should show up in your cloudinary gallery.
How to retrieve images from your cloudinary gallery
Ok, so your user was able to upload an image, but how do you retrieve that image, if you want to display it on your app. That is a great question. I won’t get to into the nitty gritty of the code, but will do my best to conceptually explain how you may do this.
Lets say for example you are creating a profile page, and after a user uploads a profile picture, you then want the image to update from the default to the image that was uploaded by the user. How do you this? Well, we could update our route to update the info in the user table after the image has been uploaded. Let’s take a look.
router.post("/upload", upload, async(req, res) => {try{const result = await uploadToCloudinary(req.file.path, { folder: "foo" });if(req.file) unlinkSync(req.file.path);//here is where the magic happens. We save the response we get back from our uploadToCloudinary method (this method if you remember was a promise and if the promise is resolved and returns back a results object) in that results object we have url property which is the url of the image from cloudinary. We can then update the field associated with image of the user using the next line of code. So next anytime we need the image and we can access it cause it is in our database. let test = await User.findOneAndUpdate({email: req.user.email}, {image:result.url}, {new:true});console.log(test);res.send(result.url);}catch(error){console.log(error);}});
Conclusion
Now there are still a few things that I don’t 100% don’t fully understand. So I realize that there may be a few errors or things that I may have misunderstood , if you think that I may have missed something or I may have explained something incorrectly, please let me know!
I also want to list this article that I used to help me understand. If my explanation doesn’t make sense, then definitely check this article out because it helped out a lot.