Image upload to Cloudinary from React Native was successful but did not provide a URL as expected

I am encountering an issue while attempting to upload an image from my React Native app to Cloudinary. Despite the image appearing on the Dashboard, I am unable to obtain the secure_url.

Below is the code snippet I am using:

  async uploadImage(photo: ImagePickerResponse, id: string): Promise<string> {
    return new Promise<string>(async (resolve, reject) => {
      const uploadPreset = 'bbbbbb';
      const cloudName = 'aaaaa';

      const body = new FormData();
      const uri: string =
        Platform.OS === 'android' ? photo.uri : photo.uri.replace('file://', '');
      const type = photo.type;
      const name = `eMia${id}.jpeg`;
      const file = {uri, type, name};
      body.append('file', file);
      body.append('upload_preset', uploadPreset);
      body.append('cloud_name', cloudName);
      const url = `https://api.cloudinary.com/v1_1/${cloudName}/image/upload`;

      fetch(url, {method: 'POST', body})
        .then((res) => {
          // Body data is not available here, resulting in missing image URL :(
          console.log(res);
          res.json();
        })
        .then((json) => {
          console.log(json);
          const url = json.secure_url;
          console.log(url);
          resolve(url);
        })
        .catch((err) => {
          reject(err);
        });
    });
  }

Server Response:

https://i.sstatic.net/kHKu7.png

Answer №1

A React Native application is designed to upload photos to the Cloudinary server and retrieve a link for the uploaded photo. The process involves two steps where the file from the Image Picker is first uploaded to the app server:

import {DBStorageInteractor} from '../interfaces';
import {BASE_URL} from '../../../config/constants';
import ImageResizer from 'react-native-image-resizer';
import {ImagePickerResponse} from 'react-native-image-picker';

    
  async uploadImage(photo: ImagePickerResponse, id: string): Promise<string> {
    return new Promise<string>(async (resolve, reject) => {
      if (!photo) {
        reject(Error('Photo is not presented'));
        return;
      }
      this.getBody(photo, id)
        .then((body) => {
          const headers = {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          };
          const options = {
            method: 'POST',
            body: body,
            headers: headers,
          };
          fetch(`${BASE_URL()}/api/images/upload`, options)
            .then((response) => response.json())
            .then((response) => {
              const secureUrl = response.secure_url;
              console.log('Uploaded successfully. Url=', secureUrl);
              resolve(secureUrl);
            })
            .catch((error) => {
              const message = `Failed uploading. Error=${error}`;
              console.log(message);
              reject(Error(message));
            });
        })
        .catch((error) => {
          reject(error);
        });
    });
  }

  getBody(photo: ImagePickerResponse, id: string): Promise<string> {
    return new Promise((resolve, reject) => {
      if (photo.fileSize < 100000) {
        resolve(
          JSON.stringify({
            img: photo.data,
            name: `eMia${id}.${this.getFileExtension(photo.uri)}`,
          })
        );
      } else {
        ImageResizer.createResizedImage(photo.uri, 400, 400, 'JPEG', 80)
          .then(({uri}) => {
            RNFS.readFile(uri, 'base64')
              .then((data) => {
                resolve(
                  JSON.stringify({
                    img: data,
                    name: `eMia${id}.${this.getFileExtension(photo.uri)}`,
                  })
                );
              })
              .catch((error) => {
                reject(error);
              });
          })
          .catch((error) => {
            reject(error);
          });
      }
    });
  }

  getFileExtension(filename) {
    return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
  }

Figure 1 illustrates how the client side of the app uploads photos from the Image Picker to the server side and receives a link to the uploaded photo.

In the second step, the server-side code written in NodeJS handles the request in two stages. Firstly, it saves the image file from the request to a temporary directory and then proceeds to upload it to the Cloudinary server:

const {Router} = require('express')
const router = Router()
const cloudinary = require('cloudinary').v2;
const fs = require('fs')

router.post('/upload', async (req, res) => {
    try {
      const file = req.body.img;
      const name = req.body.name;
      const path = `tmp/${name}`;

      fs.writeFile(path, file, 'base64', (err) => {
        if (err) {
          console.log(err);
          throw err
        }

        cloudinary.uploader.upload(path)
          .then((results) => {
            res.status(200).json(results)
          })
          .catch((error) => {
            res.status(400).json(error)
          });
      })
    } catch (error) {
      res.status(500).json(error)
    }
  }
)

Figure 2 portrays the handling of the '/upload' request on the NodeJS server.

To configure Cloudinary on the server side, you need to include the following code:

const cloudinary = require('cloudinary').v2;

require('dotenv').config();

cloudinary.config({
  cloud_name: process.env.cloud_name,
  api_key: process.env.api_key,
  api_secret: process.env.api_secret,
});

The '.env' file located in the project's root folder contains the necessary parameters as follows:

cloud_name=<Cloudinary cloud name>
api_key=<API key from the Cloudinary project settings>
api_secret=<API secret from the Cloudinary project settings>

Answer №2

Could you kindly test out the code snippet below:

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class Upload extends React.Component {
  processFile = async e => {
    var file = e.target.files[0];
    var formdata = new FormData();

    formdata.append("file", file);
    formdata.append("cloud_name", "shirly");
    formdata.append("upload_preset", "my-preset");

    let res = await fetch(
      "https://api.cloudinary.com/v1_1/emia/auto/upload",
      {
        method: "post",
        mode: "cors",
        body: formdata
      }
    );

    let json = await res.json();
    console.log(JSON.stringify(json.secure_url));
  };

  render() {
    return (
      <div>
        <h3>Upload</h3>
        <input type="file" onChange={this.processFile} />
      </div>
    );
  }
}
ReactDOM.render(<Upload />, document.getElementById("container"));

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Google API React Module

As a newcomer to React and RN, I have explored various solutions without success in my quest to retrieve Google Calendar events using the calendar v3 API. Despite trying two different approaches, neither yielded the desired results. Initially, I attempted ...

What is the ideal amount of HTML code to include in an Angular controller?

What are the implications of storing long paragraphs in controllers? For instance, if you have to display multiple lengthy paragraphs using ng-repeat, you may create a data structure like this within your controller: $scope.paragraphs['300 word para ...

I am looking for the best approach to utilize AngularJS in order to successfully submit a form to my c# backend

In the process of utilizing REST ASP.NET, I am currently in need of extracting certain values from my frontend input form so that I can search for corresponding data in my backend system coded in C#. Despite trying out the suggestions provided in this he ...

managing various states in React with the help of useReducer

Currently, I am working on a registration form that allows users to switch between displaying and concealing passwords in the input fields. The setup includes two password input sections - one for creating a password and another for re-entering it. Each in ...

Revert animation back to its initial position with the help of JavaScript

After implementing the script below, I successfully managed to shift my image to the right upon clicking: <script> var myTimer = null; function move(){ document.getElementById("fish").style.left = parseInt(document.getElementById("fish ...

When utilizing the HTML5 range input type, the value of 'this.value' may not be defined

I'm having an issue with a range input where it returns undefined when passed to my function: Here is the HTML code snippet: <div class="slidecontainer"> <label>Setpoint</label> <p>{{channel.setPoint}}</p> & ...

What is the connection between @types, TypeScript, and Webpack?

When using an exported type in a .ts file, it is necessary to import it: import {jQuery} from 'jQuery' Even after adding the import, intellisense may not work until npm install @types\jQuery is executed. If @types are installed, intellis ...

Animated Drop-Down Contact Panel

After hours of searching, I can't seem to figure out how to create a contact form that functions like the one on this website: . When you click on the contact button, a black background fades in and the form smoothly drops down using an ease function. ...

What's causing this MUI React data grid component to be rendered multiple times?

I have developed a wrapper for the MUI Data Grid Component as portrayed: Selection.tsx: import * as React from 'react'; import { DataGrid, faIR, GridSelectionModel } from '@mui/x-data-grid'; import type {} from '@mui/x-data-grid/t ...

Unexpected issue with AJAX call: browser error caused by prolonged script execution

I'm encountering an issue with my ajax call where the browser throws a "not responding due to a long-script is running" message and provides a button to stop the script. Although the URL is being fetched correctly, the console.log(4) within the succe ...

The JavaScript code is failing to retrieve any data from the API

import React, { Component } from 'react'; export class FetchData extends Component { static displayName = FetchData.name; constructor(props) { super(props); this.state = { users: [], loading: true }; } componentDidMount() { ...

React - How to dynamically change the styling of an individual table row when hovered over

I want to make the 'Position' and 'Team' cells turn pink when hovering over any cell in a row. However, my hover class is currently being applied to the first two cells of every row, rather than just the specific row being hovered over ...

Dynamic title attribute enhancement for jQuery tooltip plugin

Current Issue: I am in search of a superior alternative to the default title tag tooltip that can display multiple lines consistently across all browsers. The current dilemma is that the title tag refreshes every second. Looking for Solution: Is there a J ...

Tips for fetching element values with webdriverio and mocha

browser.getValue('#username').then(function (value) { console.log('The value of the element is: '+value); }); If you are utilizing the browser object in webdriverio with mocha runner, simply provide the element ID to re ...

Troubleshooting: Issue with connecting Node.Js to MongoDB Compass

Having trouble connecting to MongoDB Compass using Typescript, Node.js, and Mongoose. Below is my mongo connection code: const url ='mongodb://localhost:27017/BlogApp'; mongoose.connect(url) .then(() => {console.log("Connected to MongoDB")}) ...

adjusting state with React hooks

I currently have two buttons labeled create signature and Create delivery in my file PageOne.tsx. When the state is set to InDelivery, both buttons are visible. My goal is to hide the Create delivery button initially. However, when a user clicks on the cr ...

When using React and Material UI, there seems to be an issue with the Popover component where calling `setAnchorEl(null)` on the onClose event does not properly

I am encountering an issue with a Popover (imported from MaterialUI) nested inside a MenuItem (also imported from MaterialUI). The open prop for the popover is set to the boolean value of anchorEl. The onClose function is supposed to handle setting anchorE ...

All shadows in the THREE.js scene are perfectly aligned

I have taken a mix of examples from the three.js documentation and included the mesh.castShadow = true property on the meshes generated from the Glitch post-processing example. However, upon checking the jsfiddle link provided below, it's noticeable t ...

Can Phaser handle large-scale multiplayer games effectively?

Hello there, I am a newcomer to the world of game development and I am currently delving into phaser with a focus on the isometric plugin. I am curious to find out if it is feasible to design games in phaser similar to agar.io, where handling real-time mu ...

Creating a seamless checkout experience: Setting up payment method for checkout sessions

In my app, I am utilizing the stripe payment integration for processing payments. I am currently setting up a session checkout for customers with the parameter mode=payment to facilitate receiving payments for orders. However, I am unsure of how to save th ...