Blog

  • Adding Cloudinary to your React Frontend using XHR

    Adding Cloudinary to your React Frontend using XHR

    Cloudinary allows you to upload images (and videos) through API on their cloud hosting platform. It also alllows (end) user to transform images like changing height/width, adding borders, changing aspect-ratio and much more by adding parameters to their delivery URL. Cloudinary is free and I am introduced to it as an alternative to firebase from this Youtube Tutorial by Sahand Ghavidel through on of his comment.

    Note: I have made this tutorial on the basis of aforementioned project. However, you can use the code in any of your frontend projects without making any adjustment. I have written another tutorial that works with backend on this website.

    Setting Up Cloudinary

    The data you are about to copy from Cloudinary need to be in an .env file. If you have not made it yet, make one now and copy paste this sample data.

    CLOUDINARY_CLOUD_NAME = "somecloudname"
    CLOUDINARY_UPLOAD_ENDPOINT = "https://api.cloudinary.com/v1_1/somecloudname/image/upload"
    CLOUDINARY_UPLOAD_PRESET = "anyuploadpreset"
    CLOUDINARY_API_KEY = "1234567890123"
    CLOUDINARY_API_SEC = "somerandomstrings"

    Setting up Cloudinay is easy. All you need is their Cloud name (CLOUDINARY_CLOUD_NAME) API Key (CLOUDINARY_API_KEY), API secret (CLOUDINARY_API_SEC), and Upload Preset (CLOUDINARY_UPLOAD_PRESET). You will find all 4 of them in their Settings tab.

    Cloud name: Settings > Product Environments
    One thing to mention here is that your cloud name will be visible in delivery URL. You can change it under Product Environment, and it must be unique.

    API Key and API Secret: Settings > API Keys

    Upload Preset: Settings > Upload.
    Here is the one I setup for myself.

    cloudinary upload preset

     

    Adding Code to the Frontend

    This codebase is for profile page when live, user will click on their default profile picture in order to change it. A file upload popup will appear on screen where user can search for the specific picture that he wants to set it as a profile picture. The image must be of the size 2MB or under.

    We will be working with few use states.

    First one is formData, this store data like username, email, profile image, etc. We will create an instance of it to store our file.

    const [formData, setFormData] = useState({});

    “fileUploadError” will store Boolean data.

    const [fileUploadError, setFileUploadError] = useState(false);

    “file” will handle the image file that we want to upload.

    const [file, setFile] = useState(undefined);

    “filePerc” will be used to track upload progress.

      const [filePerc, setFilePerc] = useState(0);

     

    Create handleFileUpload function

    const handleFileUpload = async (file) => {}

    Ensure that the file exist by printing its name.

        console.log(`file is ${file.name}`);
        if (!file) {
          return;
        }

    Once done the next step is to make sure the file size must be under 2MB.

    const maxFileSize = 2 * 1024 * 1024;
        if (file.size > maxFileSize) {
          setFileUploadError(true);
          return;
        }

    Multiply 2 by 1024 twice to convert bytes to kilobytes and then megabytes. 

    Now, create a data object from FormData and append following information into it.

        const data = new FormData();
        data.append('file', file);
        data.append('upload_preset', import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET);
        data.append('cloud_name', import.meta.env.VITE_CLOUDINARY_CLOUD_NAME);

    Make an instance of XHR and open it with POST that will point to cloudinary upload URL. XHR provides us bunch of methods for tracking progress and checking upload status. The basic usage of XHR is to interact with server You can get more information on XHR from this documentation page.

    One thing to notice here is that I am uploading file as it is to Cloudinary using their endpoint that I have it in .env.

        const xhr = new XMLHttpRequest();
          xhr.open('POST', import.meta.env.VITE_CLOUDINARY_UPLOAD_ENDPOINT);

    We will track the upload progress of our image and store it in a new state. This is possible using an event listener on XHR upload method.

        xhr.upload.addEventListener('progress', (event) => {
          if (event.lengthComputable) {
            const progress = (event.loaded / event.total) * 100;
            console.log(`Upload progress: ${progress}%`);
            setFilePerc(Math.round(progress));
          }
        });

    We are doing simple mathematics here. We are dividing loaded progress (data uploaded) by total progress (your file size) here and multiply it by 100 to get percentage acceptable numbers.

        xhr.onload = () => {
          if (xhr.status === 200) {
            const response = JSON.parse(xhr.responseText);
            setFormData({...formData, avatar: response.url});
            console.log('File uploaded successfully', response.url);
          } else {
            console.error(`Upload failed;`, xhr.statusText);
            setFileUploadError(true);
          }
        }

    If we get the 200-status code that means our file upload is successful else, we will report it to setFileUploadError setter function.

    If we get any error in general, we will also do the same.

        xhr.onerror = () => {
          console.log('Upload failed;', xhr.statusText);
          setFileUploadError(true);
        };

    Then we send the data.

    xhr.send(data);

     

    We will call this function under useEffect:

      useEffect(() => {
        if (file) {
          handleFileUpload(file);
        }
      }, [file]);

    Now I am not attaching this function to any HTML tag, instead do it automatically once I have the image under file object.

    You can check the full code from my github.

    Hope you find this article useful, if you find any trouble or need any information, please let me know through the comment.