React前端通过Axios上传图片至Cloudinary并存MySQL时出错求助
问题描述
开发React Web应用时,表单支持文本输入和多图片上传,需求是将图片上传至Cloudinary,再把Cloudinary返回的图片URL和其他文本数据存入MySQL数据库。但目前图片无法传递到服务器,出现报错,需要修改图片输入的实现方式,求解决方案。
客户端代码
import { useState } from "react"; import Axios from 'axios'; function Verify() { // Text Inputs const [address, setAddress] = useState("") const [name, setName] = useState("") const [nic, setNIC] = useState("") // Image inputs const [nicfront, setNICFront] = useState("") const [nicback, setNICBack] = useState("") const [selfie, setSelfie] = useState("") const submitApplication = () => { console.log("start") Axios.post('http://localhost:3001/create', { address: address, name: name, nic: nic, nicfront: nicfront, nicback: nicback, selfie: selfie }).then(() => { console.log("Success") }); }; return ( <> <section> <label>Address</label> <input type="text" onChange={(event) => { setAddress(event.target.value) }} /> <label>Full Name</label> <input type="text" onChange={(event) => { setName(event.target.value) }} /> <label>NIC Number</label> <input type="text" onChange={(event) => { setNIC(event.target.value) }} /> <label>NIC Front</label> <input id="imagefront" name="imagefront" type="file" onChange={(event) => { setNICFront(event.target.value) }} /> <label>NIC Back</label> <input id="imagefront" name="imagefront" type="file" onChange={(event) => { setNICBack(event.target.value) }} /> <label>Upload a selfie with the NIC</label> <input id="imagefront" name="imagefront" type="file" onChange={(event) => { setSelfie(event.target.value) }} /> <div> <div class="control"> <button onClick={submitApplication} >Submit for Review</button> </div> </div> </section> </> ); } export default Verify;
服务端代码
const express = require("express"); const mysql = require("mysql"); const cors = require("cors"); const cloudinary = require("cloudinary").v2; require('dotenv').config(); const cl_user = process.env.CLUSER; const cl_key = process.env.CLKEY; const cl_secret = process.env.CLSECRET; const app = express() app.use(cors()); app.use(express.json()); // Cloudinary intergration cloudinary.config({ cloud_name: cl_user, api_key: cl_key, api_secret: cl_secret }); const db = mysql.createConnection({ host: 'localhost', user: 'root', password: '', database: 'blockverify' }) app.post('/create', (req, res) => { var wallet = "" var name = "" var nic = "" var nicfront = "" var nicback = "" var selfie = "" var state = 1 // Retrieving data from the client wallet = req.body.address name = req.body.name nic = req.body.nic nicfront = req.body.nicfront nicback = req.body.nicback selfie = req.body.selfie cloudinary.uploader.upload(nicfront.tempFilePath, function (err, result) { console.log("Error: ", err); console.log("Result: ", result); var frontURL = result.secure_url; }); cloudinary.uploader.upload(nicback.tempFilePath, function (err, result) { console.log("Error: ", err); console.log("Result: ", result); var backURL = result.secure_url; }); cloudinary.uploader.upload(selfie.tempFilePath, function (err, result) { console.log("Error: ", err); console.log("Result: ", result); var selfieURL = result.secure_url; }); db.query("INSERT INTO applications (wallet, name, nic, nicfront, nicback, selfie, state) VALUE (?,?,?,?,?,?,?)", [wallet, name, nic, nicfurl, nicback, selfie, state], (err, result) => { if (err) { console.log(err) } else { res.send("Inserted!") } }) }) app.listen(3001, () => { db.connect(function (err) { if (err) throw err; console.log("Connected to MySQL!"); }); });
报错信息
Error: { message: 'Missing required parameter - file', name: 'Error', http_code: 400 } Result: undefined /Users/sandaru/DATA DRIVE/Degree/L6/FYP/Project/block-verify/server/index.js:50 var frontURL = result.secure_url; ^ TypeError: Cannot read properties of undefined (reading 'secure_url') at /Users/sandaru/DATA DRIVE/Degree/L6/FYP/Project/block-verify/server/index.js:50:31 at /Users/sandaru/DATA DRIVE/Degree/L6/FYP/Project/block-verify/server/node_modules/cloudinary/lib/utils/index.js:1259:14 at IncomingMessage.<anonymous> (/Users/sandaru/DATA DRIVE/Degree/L6/FYP/Project/block-verify/server/node_modules/cloudinary/lib/uploader.js:507:9) at IncomingMessage.emit (node:events:525:35) at endReadableNT (node:internal/streams/readable:1359:12) at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
解决方案
核心问题分析
- 客户端用
event.target.value获取的是文件路径字符串,不是实际的File对象,无法用于上传 - 直接用JSON格式提交文件,服务器无法解析,必须用
FormData传递 - 服务端没配置文件解析中间件,无法接收上传的文件
- Cloudinary上传是异步操作,直接执行数据库插入会导致URL还未获取就插入,同时存在变量名错误(
nicfurl未定义)
客户端修改
- 存储File对象而非路径:
// 初始化文件状态为null const [nicfront, setNICFront] = useState(null) const [nicback, setNICBack] = useState(null) const [selfie, setSelfie] = useState(null) // 修改文件输入的onChange事件 <label>NIC Front</label> <input id="nicfront" name="nicfront" type="file" onChange={(e) => setNICFront(e.target.files[0])} /> <label>NIC Back</label> <input id="nicback" name="nicback" type="file" onChange={(e) => setNICBack(e.target.files[0])} /> <label>Upload a selfie with the NIC</label> <input id="selfie" name="selfie" type="file" onChange={(e) => setSelfie(e.target.files[0])} />
- 用FormData提交数据:
const submitApplication = () => { const formData = new FormData(); formData.append('address', address); formData.append('name', name); formData.append('nic', nic); formData.append('nicfront', nicfront); formData.append('nicback', nicback); formData.append('selfie', selfie); Axios.post('http://localhost:3001/create', formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(() => console.log("Success")) .catch(err => console.error(err)); };
服务端修改
- 安装multer中间件:
npm install multer
- 配置multer并修改路由:
const multer = require('multer'); // 用内存存储文件,方便上传到Cloudinary const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); // 修改路由,添加multer处理多文件上传 app.post('/create', upload.fields([ { name: 'nicfront', maxCount: 1 }, { name: 'nicback', maxCount: 1 }, { name: 'selfie', maxCount: 1 } ]), async (req, res) => { try { const { address, name, nic } = req.body; const state = 1; // 异步上传图片到Cloudinary,确保获取到URL后再操作数据库 const frontRes = await cloudinary.uploader.upload(req.files.nicfront[0].path, { resource_type: 'auto' }); const backRes = await cloudinary.uploader.upload(req.files.nicback[0].path, { resource_type: 'auto' }); const selfieRes = await cloudinary.uploader.upload(req.files.selfie[0].path, { resource_type: 'auto' }); // 插入数据库,传入正确的图片URL db.query( "INSERT INTO applications (wallet, name, nic, nicfront, nicback, selfie, state) VALUES (?,?,?,?,?,?,?)", [address, name, nic, frontRes.secure_url, backRes.secure_url, selfieRes.secure_url, state], (err, result) => { if (err) { console.error(err); return res.status(500).send("数据库插入失败"); } res.send("提交成功!"); } ); } catch (err) { console.error(err); res.status(500).send("图片上传失败"); } });
额外注意事项
- 确保Cloudinary的环境变量配置正确
- 前端可添加验证逻辑,确保用户选择所有文件后再允许提交
- 测试时需选择实际文件,避免空文件导致报错
内容的提问来源于stack exchange,提问作者Sandaru Fernando
相关产品推荐
相关产品推荐

