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
相关产品推荐
相关产品推荐

