尝试从 React 组件将图片上传到图像托管服务时出现 CORS 问题

问题描述 投票:0回答:4

我正在使用 Express JS 和 CRA 开发 React 应用程序。我想尽可能“简单”地将图片上传到图像托管服务(例如 imgur、imageshack 或 freeimage.host),并获取其 URL,以便我可以在我的应用程序上显示它。但我在 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 个键:值的 POST 请求),但我无法让我的控制器使用 fetch。它显然不被识别为函数。我确实按照许多教程中的建议安装了“node-fetch”包,但导入它似乎很困难:

  • 我不能这样做
    const fetch = require("node-fetch")
    ,这会返回一个错误,指出不支持这种形式的导入
  • 我不能这样做
    import fetch from "node-fetch"
    ,这会返回另一个错误,因为我的控制器不是模块
  • 我可以做到
    const fetch = import ("node-fetch")
    ,但是 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。

提前感谢您的帮助

node.js reactjs express cors fetch
4个回答
0
投票

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

我认为我的第二个最大的错误是 1/ 当我可以使用 axios 时尝试坚持使用 fetch 2/ 当存在其他图像托管 API 时,坚持使用 freeimage.host。

当我尝试使用 axios 时,事情变得更加顺利,然后过了一会儿,当我放弃 freeimage.host 转而使用 imgbb.com 时。

这就是我所做的。首先,使用 npm i axios 和 npm i form-data 安装 axios 和 form-data。从那里,我可以使用我习惯的

require('axios')
语法导入它。您还想请求 API 密钥。对于 imgbb.com,您需要创建一个帐户并在此处请求密钥 https://api.imgbb.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 响应将使您看到图像 URL 可以在 response.data.data.url.

中找到

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

我希望这对某些人有用。


0
投票

我遇到了同样的错误,然后我使用了其他 api 方法,它是免费的、相似的且易于使用

const handleImageChange = async (e) => { const 文件 = 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

”, 表单数据, { 标题:{ '内容类型':'多部分/表单数据', }, } );

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

};


-1
投票

如果响应标头不包含 access-control-allow-origin“origin”或“*”,浏览器会阻止在不同服务器上发送的请求。

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

将图像从 React 上传到 Node。然后,使用 Axios、node-fetch 等将图像上传到“freeimage.host”


-1
投票

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

希望能帮到你

© www.soinside.com 2019 - 2024. All rights reserved.