You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 16:35:26