Node.js后端通过router.get同时向React前端发送图片与JSON数据问题
解决Node.js GET接口同时返回图片与JSON数据的问题
你当前的问题核心在于:HTTP单次响应只能对应一种Content-Type,res.sendFile和res.json会互相覆盖响应内容;同时手动设置multipart/form-data却没有正确构建多部分数据的边界,导致解析失败。下面提供三种可行的实现方案:
方案一:将图片转为Base64嵌入JSON(最简单)
把图片文件编码成Base64字符串,作为用户数据的一个字段一起返回,前端可以直接把Base64字符串作为img标签的src属性使用。
后端代码示例:
const fs = require('fs').promises; // 用Promise版fs方便异步处理 router.get("/userdata/:id", async (req, res) => { const id = req.params.id; const userinfo = await Users.findOne({ where: { id: id }, attributes: { exclude: ["password", "emailverification"] }, }); // 读取图片文件并转为Base64 const imagePath = path.join(__dirname, "..", userinfo.pfp); const imageBuffer = await fs.readFile(imagePath); const base64Image = `data:image/png;base64,${imageBuffer.toString('base64')}`; // 把Base64图片加到用户数据里 userinfo.dataValues.pfpBase64 = base64Image; res.json(userinfo); });
前端使用示例(React):
// 假设请求后拿到response数据 <img src={response.pfpBase64} alt="用户头像" />
方案二:正确构建multipart/form-data响应(复杂但符合多部分数据规范)
需要手动构建multipart响应,设置边界分隔符,分别发送JSON和图片数据。这种方式前端需要解析multipart响应,适合对请求次数有严格要求的场景。
后端代码示例:
const fs = require('fs').promises; const { createReadStream } = require('fs'); router.get("/userdata/:id", async (req, res) => { const id = req.params.id; const userinfo = await Users.findOne({ where: { id: id }, attributes: { exclude: ["password", "emailverification"] }, }); const imagePath = path.join(__dirname, "..", userinfo.pfp); const boundary = `----UserBoundary${Date.now()}`; // 设置响应头 res.setHeader('Content-Type', `multipart/form-data; boundary=${boundary}`); // 发送JSON部分 res.write(`--${boundary}\r\n`); res.write('Content-Disposition: form-data; name="userinfo"\r\n'); res.write('Content-Type: application/json\r\n\r\n'); res.write(JSON.stringify(userinfo)); res.write('\r\n'); // 发送图片部分 res.write(`--${boundary}\r\n`); res.write('Content-Disposition: form-data; name="pfp"; filename="avatar.png"\r\n'); res.write('Content-Type: image/png\r\n\r\n'); // 流式发送图片文件 const stream = createReadStream(imagePath); await new Promise((resolve, reject) => { stream.pipe(res, { end: false }); stream.on('end', () => { // 结束multipart res.write(`\r\n--${boundary}--\r\n`); res.end(); resolve(); }); stream.on('error', reject); }); });
前端解析示例(React中使用fetch):
async function fetchUserData(id) { const response = await fetch(`/userdata/${id}`); const formData = await response.formData(); const userinfo = JSON.parse(formData.get('userinfo')); const pfpBlob = formData.get('pfp'); const pfpUrl = URL.createObjectURL(pfpBlob); return { userinfo, pfpUrl }; }
方案三:拆分两个独立接口(最常用、最稳定)
没必要强行一次请求返回所有数据,拆分两个接口分别获取用户信息和头像,前端可以并行请求,实现上最简单且易维护。
用户信息接口:
router.get("/userdata/:id", async (req, res) => { const id = req.params.id; const userinfo = await Users.findOne({ where: { id: id }, attributes: { exclude: ["password", "emailverification"] }, }); res.json(userinfo); });
头像接口:
router.get("/useravatar/:id", async (req, res) => { const id = req.params.id; const userinfo = await Users.findOne({ where: { id: id }, attributes: ["pfp"], }); res.set("Content-Type", "image/png"); res.sendFile(path.join(__dirname, "..", userinfo.pfp)); });
前端并行请求示例:
async function getUserData(id) { const [userRes, avatarRes] = await Promise.all([ fetch(`/userdata/${id}`).then(res => res.json()), fetch(`/useravatar/${id}`) ]); const avatarUrl = URL.createObjectURL(await avatarRes.blob()); return { ...userRes, avatarUrl }; }
内容的提问来源于stack exchange,提问作者Tijmi
相关产品推荐
相关产品推荐

