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

React无法读取Node.js存储为Buffer的图片:上传方式一正常一异常

图片存储后React客户端无法通过Base64正常显示的问题

我在数据库的不同文档中存储了两次图片,存储方法99%相同(差异见附带代码)。两者均已正确存储,但React客户端读取时,其中一个能正常显示,另一个无法正常读取。

服务器端代码(NodeJS + Express + Mongoose + Multer + Sharp)

// * Upload avatar 
router.post(
  "/users/avatar",
  multerUpload.single("avatar"),
  async (req, res) => {
       const buffer = await sharp(req.file.buffer).resize({ width: 200, height: 200 }).png().toBuffer();
    req.user.avatar = buffer;
    
    req.user.avatar = req.file.buffer;
    await req.user.save();
    res.send();
  },
  (error, req, res, next) => {
    res.status(400).send({ error: error.message });
  }
);

数据库中头像以二进制数据存储

客户端上传及显示代码(React)

function Foo() {
  const [file, setFile] = useState();
  const [image, setImage] = useState("");
  const filePickerRef = useRef();

  const pickImageHandler = () => {
    filePickerRef.current.click();
  };
  const pickHandler = (e) => {
     let  pickedFile = e.target.files[0];
      setFile(pickedFile);
  };

  const handleUploadProfileImage = async () => {
    // 本次上传用自定义对象避免打包问题
    // 另一次上传是构建完整对象,甚至没在请求头中指定content-type
    const obj = {
      test: "its seems that in order to use multypart contnet type i must use multy type ",
      avatar: file,
    };

    await usersApi.userUploadAvatar.post("", obj, {
      headers: {
        Authorization: `Bearer ${JSON.parse(localStorage.getItem("token"))}`,
        "Content-Type": "multipart/form-data",
      },
    });
  };


  const getUploaded = async () => {
    // 该接口获取存储的所有数据
    const { data } = await router.get("62cb2b404e986b25cc73dc74");
    setImage(data.avatar)
  }

  return (
    <div className="container">
      <div onClick={pickImageHandler}>upload img</div>
      <input
        onChange={pickHandler}
        ref={filePickerRef}
        type="file"
        style={{ display: "none" }}
        accept=".jpg,.png,.jpeg"
      />
      <div onClick={handleUploadProfileImage}>
        <h1> Uploaded image to DB</h1>
      </div>

    <div onClick={getUploaded}>
        <h1>Fetch Image</h1>
        <img src={`data:image/png;base64, ${Buffer.from(avatar).toString("base64")}`} alt="" /> {/* 此上传方式下无法工作 */}
        <img src={avatar}  alt="" />
    </div>

    </div>
  );
}

问题说明

采用此上传方式时,通过Base64格式转换显示图片的方式无法正常工作。


问题排查与修复方案

1. 服务器端冗余赋值导致图片处理无效

你在服务器端先通过Sharp对图片做了压缩转PNG处理,但随后又将req.user.avatar重新赋值为原始的req.file.buffer,等于Sharp的处理完全没生效。如果另一个正常的存储方式保留了Sharp处理后的buffer,两者存储的二进制数据格式会有差异(比如一个是统一的PNG,一个是用户上传的原格式JPG/PNG),导致前端硬编码data:image/png;base64时格式不匹配。

修复代码:

// * Upload avatar 
router.post(
  "/users/avatar",
  multerUpload.single("avatar"),
  async (req, res) => {
    const buffer = await sharp(req.file.buffer).resize({ width: 200, height: 200 }).png().toBuffer();
    req.user.avatar = buffer; // 只保留Sharp处理后的buffer
    await req.user.save();
    res.send();
  },
  (error, req, res, next) => {
    res.status(400).send({ error: error.message });
  }
);

2. 客户端上传时错误设置Content-Type

手动设置"Content-Type": "multipart/form-data"会导致Axios无法自动生成正确的boundary分隔符,服务器接收的文件数据可能损坏或格式错误。而且你直接传递普通对象而非FormData,文件无法被正确解析。

修复上传代码:

const handleUploadProfileImage = async () => {
  const formData = new FormData();
  formData.append("test", "its seems that in order to use multypart contnet type i must use multy type ");
  formData.append("avatar", file);

  await usersApi.userUploadAvatar.post("", formData, {
    headers: {
      Authorization: `Bearer ${JSON.parse(localStorage.getItem("token"))}`,
      // 移除手动设置的Content-Type,Axios会自动处理
    },
  });
};

3. 客户端显示时的Base64转换逻辑错误

从后端获取的data.avatar如果已经是Mongoose自动转换的Base64字符串,再用Buffer.from处理会导致二次编码,生成无效的Base64。需要根据后端返回的数据类型调整显示逻辑:

情况1:后端返回Base64字符串

const getUploaded = async () => {
  const { data } = await router.get("62cb2b404e986b25cc73dc74");
  setImage(`data:image/png;base64, ${data.avatar}`);
}

// 渲染时直接使用
<img src={image} alt="头像" />

情况2:后端返回原始二进制数据
需要设置请求的responseType为arraybuffer,再转换为Base64:

const getUploaded = async () => {
  const { data } = await router.get("62cb2b404e986b25cc73dc74", {
    responseType: 'arraybuffer'
  });
  const base64 = Buffer.from(data.avatar).toString('base64');
  setImage(`data:image/png;base64, ${base64}`);
}

// 渲染时直接使用
<img src={image} alt="头像" />

内容的提问来源于stack exchange,提问作者Bennyh961

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 06:54:45