如何在自建Node.js API中引入本地图片并在React中展示
解决方案:Node.js API 配置图片并在 React 中展示
你在 Node.js 后端用 import 导入图片的方式行不通——后端没有前端打包工具(比如 Webpack)处理资源导入逻辑,得换用静态资源托管的方式实现图片访问,以下是具体步骤:
一、先实现现有静态图片的展示
修改 API.js
- 移除无效的图片
import语句 - 用 Express 的
static中间件托管assets文件夹,让前端能通过 HTTP 请求直接访问里面的图片 - 在 operators 数组的
img字段填写图片相对于静态目录的路径
const app = require("express")() const cors = require("cors") const PORT = 8000 app.use(cors({})) // 托管 assets 文件夹为静态资源,前端可通过 /xxx 路径访问 assets 下的文件 app.use(express.static("./assets")) app.get("/", (req, res) => { res.send({ operators: [ { img: "/operators/thermite.png", // 对应本地 assets/operators/thermite.png name: "Thermite", side: "Attack", }, { img: "/operators/ash.png", // 需确保本地有该图片文件 name: "Ash", side: "Attack", }, { img: "/operators/vigil.png", // 需确保本地有该图片文件 name: "Vigil", side: "Defense", }, ], }) }) app.listen(PORT, () => console.log(`server running on ${PORT}`))
修改 App.jsx
把显示 op.img 的 <p> 标签换成 <img> 标签,拼接完整的图片 URL 加载资源:
import { useEffect, useState } from "react" function App() { const [operators, setOperators] = useState([]) useEffect(() => { const fetchData = async () => { const res = await fetch("http://127.0.0.1:8000") const data = await res.json() setOperators(data.operators) } fetchData() }, []) // 加空依赖数组,避免无限触发请求 return ( <> {operators.map((op, idx) => { return ( <div key={idx} className="flex flex-col"> <div className="flex gap-4 px-4 items-center"> {op.img && ( <img src={`http://127.0.0.1:8000${op.img}`} alt={op.name} style={{ width: "60px", height: "60px", objectFit: "cover" }} /> )} <p>{op.name}</p> <p>{op.side}</p> </div> </div> ) })} </> ) } export default App
二、添加图片上传功能(动态关联干员)
如果需要实现用户上传图片并关联到干员,用 multer 中间件处理文件上传:
1. 安装依赖
npm install multer
2. 修改 API.js,添加上传接口
const app = require("express")() const cors = require("cors") const multer = require("multer") const PORT = 8000 app.use(cors({})) app.use(express.static("./assets")) app.use(express.json()) // 配置 multer,设置上传文件的存储路径和命名规则 const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, "./assets/operators") // 上传的图片存在 assets/operators 目录 }, filename: (req, file, cb) => { // 用干员名字作为文件名,避免重复 const operatorName = req.body.name.toLowerCase() const ext = file.originalname.split(".").pop() cb(null, `${operatorName}.${ext}`) } }) const upload = multer({ storage: storage }) // 模拟存储干员数据的数组(实际项目建议用数据库) let operators = [ { img: "/operators/thermite.png", name: "Thermite", side: "Attack", }, { name: "Ash", side: "Attack", }, { name: "Vigil", side: "Defense", }, ] // 获取干员列表接口 app.get("/", (req, res) => { res.send({ operators }) }) // 上传图片并关联干员的接口 app.post("/upload-operator-img", upload.single("img"), (req, res) => { const { name } = req.body const imgPath = `/operators/${req.file.filename}` // 更新对应干员的 img 字段 const targetOp = operators.find(op => op.name === name) if (targetOp) { targetOp.img = imgPath res.send({ success: true, img: imgPath }) } else { res.send({ success: false, message: "未找到对应干员" }) } }) app.listen(PORT, () => console.log(`server running on ${PORT}`))
3. 前端添加上传组件(App.jsx 示例)
import { useEffect, useState } from "react" function App() { const [operators, setOperators] = useState([]) useEffect(() => { fetchOperators() }, []) // 获取干员列表 const fetchOperators = async () => { const res = await fetch("http://127.0.0.1:8000") const data = await res.json() setOperators(data.operators) } // 处理图片上传 const handleUpload = async (e) => { e.preventDefault() const formData = new FormData(e.target) try { const res = await fetch("http://127.0.0.1:8000/upload-operator-img", { method: "POST", body: formData }) const data = await res.json() if (data.success) { fetchOperators() // 重新获取列表更新UI e.target.reset() } } catch (err) { console.error("上传失败", err) } } return ( <> {/* 上传表单 */} <div className="p-4 border-b"> <form onSubmit={handleUpload} encType="multipart/form-data" className="flex gap-2 items-center" > <input type="text" name="name" placeholder="输入干员名字" required className="px-2 py-1 border" /> <input type="file" name="img" accept="image/png, image/jpeg" required /> <button type="submit" className="px-4 py-1 bg-blue-500 text-white rounded" > 上传图片 </button> </form> </div> {/* 干员列表 */} {operators.map((op, idx) => { return ( <div key={idx} className="flex flex-col"> <div className="flex gap-4 px-4 items-center py-2"> {op.img && ( <img src={`http://127.0.0.1:8000${op.img}`} alt={op.name} style={{ width: "60px", height: "60px", objectFit: "cover" }} /> )} <p>{op.name}</p> <p>{op.side}</p> </div> </div> ) })} </> ) } export default App
内容的提问来源于stack exchange,提问作者Pulkit
相关产品推荐
相关产品推荐

