当试图将图片上传到React组件的图像托管服务时,CORS问题

发布于 2025-02-07 23:51:44 字数 4204 浏览 1 评论 0原文

我正在使用CRA使用Express JS的React应用程序。我想将图片上传到图像托管服务(例如imgur,imageshack或freeimage.host)为“简单地”,然后将其返回,以便我可以在应用程序上显示它。但是我在CORS上遇到了很多麻烦。

作为记录,我对CORS有基本的一般理解,以及它如何防止对其他域进行请求。我还看到许多人在CORS上遇到问题,并且似乎是CORS必须由后端服务器来处理,而不是前端。我不太了解。

为了记录,从本质上讲,这是我尝试上传图片的测试组件(React)。

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>
    )
}

这是我的server.js(我删除了与问题无关的所有内容,例如我所有的所有其他路线等),

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}.`);
});

我不太了解的一件事是,如果需要由我的后端服务器处理CORS,那就意味着图像上传应该从我的后端服务器完成?如果是这样,怎么样?因为我试图使用控制器进行专用路线,以尝试提出一个简单的请求(没有图片,只有一个带有1个键的帖子请求:value),但我无法让我的控制器使用fetch。显然,它没有被认为是函数。 那样

  • 正如许多教程中建议的 错误指出这种导入形式不支持
  • 我不能从“ node-fetch” eximpt fetch,这返回另一个错误,因为我的控制器不是
  • 我可以做const fetch =的 模块导入(“ node-fetch”),但随后未被视为函数,

这是我的控制器的一部分(路线正常工作,我可以称呼此API,API本身不起作用) 。

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,
    });
  }
};

我有点不知所措。我觉得这不应该很复杂,但是我觉得被卡住了。 我只想能够上传图片并获取其URL。

事先感谢您的帮助

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

似最初 2025-02-14 23:51:44

所以我最终自己解决了问题。回答我自己的问题,以便其他遇到类似斗争的人可以找到出路。

我认为我最大的两个错误是
1/当我可以使用Axios时,试图坚持fetch
2/粘贴免费图像。

当我尝试与Axios合作时,情况变得更加顺畅,然后稍后放弃freeimage.host for imgbb.com。

这就是我所做的。首先,使用NPM I Axios和NPM I Form-Data安装Axios和Form-Data。从那里,我可以使用require('axios')语法导入它。您还想请求API键。对于imgbb.com,您需要创建一个帐户并在此处请求键 https://api.imgbbbb.com/

这是使它起作用的重要代码,

const fs = require("fs");
const axios = require("axios");
const formData = require("form-data");

const uploadAxios = () => {

try {
    console.log("ping !");

    const myFile = fs.readFileSync(
      `YOUR_FILE_LOCATION_ON_YOUR_COMPUTER`, { encoding: "base64" }
    );

    const myForm = new formData();
    myForm.append("image", myFile);

    await axios
      .post(
        `https://api.imgbb.com/1/upload?key=YOUR_KEY_YOU_GOT_ON_THE_API_PAGE`,
        myForm,
        {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        }
      )
      .then((response) => {
        console.log("API response ↓");
        console.log(response);
      })
      .catch((err) => {
        console.log("API error ↓");
        console.log(err);

        if (err.response.data.error) {
          console.log(err.response.data.error);
          //When trouble shooting, simple informations about the error can be found in err.response.data.error so it's good to display it
        }
        
      });
  } catch (error) {
    console.log(error);
  }

这是我可以使它起作用的最简单的。可能还有其他方法,具有更好的格式或更精确的选项,但这对我来说有用。在控制台中显示API响应将使您看到可以在 wenders.data.data.url 中找到图像URL。

当然,所有这些都是在我的Express服务器中的一个控制器中完成的(因为原始问题是CORS),

希望这对某些人有用。

so I ended up solving the thing by myself. Responding to my own question so that other people who run into similar struggles can find a way out.

I think my 2 biggest mistake was
1/ trying to stick with fetch when I could use axios just fine
2/ sticking with freeimage.host when other image hosting APIs exist.

Things went a lot smoother when I tried to work with axios, and then a bit later when I gave up on freeimage.host for imgbb.com instead.

Here's what I did. First, install axios and form-data using npm i axios and npm i form-data. And from there, I can import it using the require('axios') syntax I'm used to. You also want to request a API key. For imgbb.com you need to create an account and request a key here https://api.imgbb.com/

And here's the important bits of code to make it work

const fs = require("fs");
const axios = require("axios");
const formData = require("form-data");

const uploadAxios = () => {

try {
    console.log("ping !");

    const myFile = fs.readFileSync(
      `YOUR_FILE_LOCATION_ON_YOUR_COMPUTER`, { encoding: "base64" }
    );

    const myForm = new formData();
    myForm.append("image", myFile);

    await axios
      .post(
        `https://api.imgbb.com/1/upload?key=YOUR_KEY_YOU_GOT_ON_THE_API_PAGE`,
        myForm,
        {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        }
      )
      .then((response) => {
        console.log("API response ↓");
        console.log(response);
      })
      .catch((err) => {
        console.log("API error ↓");
        console.log(err);

        if (err.response.data.error) {
          console.log(err.response.data.error);
          //When trouble shooting, simple informations about the error can be found in err.response.data.error so it's good to display it
        }
        
      });
  } catch (error) {
    console.log(error);
  }

This the simplest I could make it work. There are probably other approaches, with better formatting or more precise options, but this works for me. Displaying the API response in the console will let you see that the image URL can be found in response.data.data.url.

Of course, all this is done in one of my controllers in my Express server (since the original issue is CORS)

I hope this'll be useful to some.

别把无礼当个性 2025-02-14 23:51:44

我遇到了相同的错误,然后使用了其他API方法,并且可以使用它是免费的,相似且易于使用的

 const handleImageChange = async (e) => {
    const file = e.target.files[0];
  
    if (file && file.type.startsWith("image/")) {
      const formData = new FormData();
      formData.append("image", file); // Append the image file
  
      try {
        const response = await axios.post(
          "https://api.imgbb.com/1/upload?key=you_key
",
          formData,
          {
            headers: {
              'Content-Type': 'multipart/form-data',
            },
          }
        );
  
        console.log(response.data); // Log the response data
      } catch (error) {
        console.error("Error uploading image:", error);
      }
    } else {
      console.error("Selected file is not an image.");
    }
  };

i was facing the same error then i used an other api method and that works it was free and similar and easy to use

 const handleImageChange = async (e) => {
    const file = e.target.files[0];
  
    if (file && file.type.startsWith("image/")) {
      const formData = new FormData();
      formData.append("image", file); // Append the image file
  
      try {
        const response = await axios.post(
          "https://api.imgbb.com/1/upload?key=you_key
",
          formData,
          {
            headers: {
              'Content-Type': 'multipart/form-data',
            },
          }
        );
  
        console.log(response.data); // Log the response data
      } catch (error) {
        console.error("Error uploading image:", error);
      }
    } else {
      console.error("Selected file is not an image.");
    }
  };
请别遗忘我 2025-02-14 23:51:44

如果响应标头不包含访问控制“ Origin”或“*”,则浏览器会阻止在其他服务器上发送的请求。

您可以从节点服务器上传图像。

上传图像从反应到节点。然后,使用Axios,node-fetch或等等将图像上传到“ freeimage.host”。

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.

空气里的味道 2025-02-14 23:51:44

要上传文件,我个人使用 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;

我这样用它:

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
         }
    }

我希望它能有所帮助

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文