Home > Mobile >  CORS issue when trying to upload a picture to image hosting service from react component
CORS issue when trying to upload a picture to image hosting service from react component

Time:06-16

I'm working on a React app with Express JS, using CRA. I would like to upload a picture to image hosting service (such as imgur, imageshack or freeimage.host) as "simply" as possible, and get its URL back so that I can display it on my app. But I'm having a lot of trouble with CORS.

For the record, I have a basic general understanding of CORS, and how it prevents from doing request to a different domain. I've also seen many people have issues with CORS, and the gist of it seems to be that CORS must be handled by the backend server, and not the frontend. Which I don't understand very well.

For the record, here's essentially my test component (React) in which I'm trying to upload the picture.

import React, { useState, useContext } from 'react'
import { Form } from 'react-bootstrap'

export default function testUpload() {

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

    const handleEditForm = inputEvent => {
        const { files } = inputEvent.target;
    
        console.log(files[0]);
        setFile(files[0]);
    }
  
    const apiSendPic = async () => {
        
      const formData = new FormData()
      formData.append('file', file)
      formData.append("firstName", "Yamakyu")

      await fetch(
        'https://freeimage.host/api/1/upload?key=6d207e02198a847aa98d0a2a901485a5',
        {
          method: 'POST',
          body: formData,
        }
      )
      .then((response) => response.json())
      .then((result) => {
        console.log('Success:', result);
      })
      .catch((error) => {
        console.error('Error:', error);
      });
    }

    
    return (
        <div>
            <button onClick={apiSendPic}>Upload</button>

            <Form>
                <Form.Group controlId="fileName">
                    <Form.Label className='VerticalLabel'>Photo ou image :</Form.Label> <br />
                    <Form.Control
                        className='LargeInput'
                        type="file"
                        name='image'
                        onChange={handleEditForm}
                        size="lg" 
                    />
                </Form.Group>
            </Form>
        </div>
    )
}

And here's my server.js (I removed everything irrelevant to the question, such as all my other routes and such)

const path = require("path");
const express = require("express");
const app = express();
const db = require("./models");
const cors = require("cors");
const PORT = process.env.PORT || 8081;


const corsOptions = {
  origin: "*",
  credentials: true, //access-control-allow-credentials:true
  optionSuccessStatus: 200,
};
app.use(cors(corsOptions));

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use(express.static(path.join(__dirname, "./build_front")));

const RouteTest = require("./Routes/RouteTest");
app.use("/api/test", RouteTest);

db.sequelize.sync();

app.use("/api/reset", userController.isLoggedIn, (req, res) => {
  db.sequelize
    .sync({ force: true })
    .then(() => {
      console.log("DROP and re-sync db.");
    })
    .catch((err) => console.log(`Error while dropping/syncing db : ${err}`));

  return res.status(200).json({
    message: "Database droped",
    needLogout: false,
  });
});


app.get("*", (_, res) => res.sendFile("index.html", { root: "build" }));

app.listen(PORT, () => {
  console.log(`Listening to port ${PORT}.`);
});

One thing I don't understand very well is, if CORS needs to be handled by my backend server, does that mean the image upload should be done from my backend server ? And if so, how ? Because I tried to make a dedicated route with a controller to try to make even a simple request (no picture, just a single POST request with 1 key:value), but I could not get my controller to use fetch. It is apparently not recognized as a function. I did install the "node-fetch" package, as recommanded in many tutorials, but importing it seems to be difficult :

  • I cannot do const fetch = require("node-fetch"), this returns an error stating that this form of import is not supported
  • I cannot do import fetch from "node-fetch", this returns another error, as my controller is not a module
  • I can do const fetch = import ("node-fetch"), but then fetch is not recognized as a function

Here's a piece of my controller (the route works fine, I can call this api just fine, the api itself doesn't work).

const db = require("../models");
const fetch = import("node-fetch");

exports.upload = async (req, res) => {
  try {
    await fetch("https://freeimage.host/api/1/upload?key=6d207e02198a847aa98d0a2a901485a5", {
      method: "POST",
      headers: {
        "Content-type": "application/json",
      },
      body: JSON.stringify({
        name: "yamakyu",
      }),
    })
      .then((res) => res.json())
      .then((data) => {
        console.log("API response ↓");
        console.log(data.message);
      });
  } catch (error) {
    console.log("↓ ------- ERROR ↓");
    console.log(error);
    return res.status(500).json({
      error,
    });
  }
};

I'm a bit at a loss. I feel like this shouldn't be complicated, but I feel stuck. I just want to be able to upload a picture, and get its URL.

Thanks in advance for your help

CodePudding user response:

The browser blocks the request which is sent on a different server if the response header does not contain access-control-allow-origin "origin" or "*".

You can upload images from the node server.

Upload image from React to Node. Then, upload the image to "freeimage.host" using Axios, node-fetch or, etc.

CodePudding user response:

to upload file I personally use express-fileupload.

    // conf/express.js
    const fileUpload = require("express-fileupload");
    const cors = require("cors");
    //...
    app.use(fileUpload());
    app.use(cors());
    app.options("*", cors());
    //...
    module.exports = app;

And I use it like that:

const fileExists = require("file-exists");
    const path = require("path");
    const app = require("./conf/express");
    
    //...
    // get an image
    app.get("/images/:id", async (req, res) => {
        return res.sendFile(path.resolve("./images/"   req.params.id));
    });
    
    // post an image
    app.post("/images", async (req, res) => {
        if (!req.files) {
            // return error
        }
        if (req.files.file) {
            let file = req.files.file;
            if (!["image/jpeg", "image/png", "image/heic", "application/octet-stream"].includes(file.mimetype)) {
                // return error (or not it depend if you only want an image)
            }
            let newFileName = Utils.generateId(30); //random name, or not
            file.mv(path.resolve("images/"   newFileName   ".png"), function (err) {
                if (err) {
                    console.error(err);
                }
                // process.env.MYSELF_URL is http://localhost:3000 for example
                let imagePath = process.env.MYSELF_URL   "/images/"   newFileName   ".png";
                // return success with image path that correspond to 
             });
         } else {
             // return error
         }
    }

I hope it can help

  • Related