从SQLite读取BLOB图片在React中显示为黑色的问题排查
问题描述
在React项目中从后端接口获取SQLite数据库里以BLOB类型存储的JPEG图片缓冲区数据,经过Blob转换、FileReader转Base64操作后,图片占位框能显示,但图片内容呈黑色。
项目代码
import React from 'react'; import { useEffect } from 'react'; import { useState } from 'react'; const Product = () => { const [data, setData] = useState([]); const [image, Setimage] = useState(null); useEffect(() => { fetch('http://localhost:3000/data') .then(response => (response.json())) .then(result => setData(result.data)) .catch(error => (error)); }, []) useEffect(() => { if (!data || !data.length) return; Promise.all(data.map(d => tobase64(d.Image) .then(base64 => ({id: d.id, base64})) .catch(error => console.error(error)) )) .then(images => Setimage(images)) .catch(error => console.error(error)); }, [data]); const tobase64 = file => { const blobIM = new Blob([file.buffer], {type: "image/jpeg"}); const reader = new FileReader(); return new Promise((resolve, reject) => { reader.readAsDataURL(blobIM); reader.onload = () => { const imageType = blobIM.type; if (imageType === "image/jpeg" || imageType === "image/png") { resolve(reader.result.split(',')[1]); } else { reject(new Error("Invalid image type. Only PNG and JPG are supported.")) } }; reader.onerror = error => reject(error); }); }; return ( <div className='w-full flex justify-between flex-col'> <h1 className='text-white text-[40px]'>Data from back end test</h1> <ul> {data.map(data => ( <li className='text-secondary' key={data.id}>{data.Description}</li> ))} </ul> <div> {image && image.map(img => ( <div className='w-full flex' key={img.id}> <div> <img className='flex h-64 w-64' src={`data:image/jpeg;base64,${img.base64}`} /> </div> </div> ))} </div> </div> ) } export default Product
获取到的图片数据
Image: ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 01 00 01 00 00 ff db 00 43 00 0d 09 0a 0b 0a 08 0d 0b 0a 0b 0e 0e 0d 0f 13 20 15 13 12 12 13 27 1c 1e 17 ... 223858 more bytes
调试过程
- 最初尝试用FileReader直接转缓冲区为Base64报错
- 转为Blob后结果为字符串
- 经useEffect处理后,图片占位框显示但内容为黑色
原因分析及解决方案
核心原因
后端返回的d.Image是空格分隔的十六进制字符串,但代码错误地将其当作带有buffer属性的二进制对象处理,导致生成的Blob内容是字符串的ASCII编码而非图片的二进制数据,最终图片无法正确渲染,仅显示黑色占位。
修复步骤
- 新增工具函数,将十六进制字符串转换为
Uint8Array格式的二进制数据 - 修改
tobase64函数逻辑,先处理十六进制字符串,再生成Blob和Base64编码
修复后的完整代码
import React from 'react'; import { useEffect, useState } from 'react'; const Product = () => { const [data, setData] = useState([]); const [images, setImages] = useState(null); useEffect(() => { fetch('http://localhost:3000/data') .then(response => response.json()) .then(result => setData(result.data)) .catch(error => console.error(error)); }, []); useEffect(() => { if (!data || !data.length) return; Promise.all(data.map(d => toBase64(d.Image) .then(base64 => ({ id: d.id, base64 })) .catch(error => console.error(error)) )) .then(processedImages => setImages(processedImages)) .catch(error => console.error(error)); }, [data]); // 将空格分隔的十六进制字符串转为Uint8Array二进制数组 const hexStringToUint8Array = (hexStr) => { const cleanHex = hexStr.replace(/\s+/g, ''); const hexPairs = cleanHex.match(/.{1,2}/g) || []; return new Uint8Array(hexPairs.map(pair => parseInt(pair, 16))); }; const toBase64 = (hexImageData) => { const imageBuffer = hexStringToUint8Array(hexImageData); const blobIM = new Blob([imageBuffer], { type: "image/jpeg" }); const reader = new FileReader(); return new Promise((resolve, reject) => { reader.readAsDataURL(blobIM); reader.onload = () => { const imageType = blobIM.type; if (imageType === "image/jpeg" || imageType === "image/png") { resolve(reader.result.split(',')[1]); } else { reject(new Error("Invalid image type. Only PNG and JPG are supported.")); } }; reader.onerror = error => reject(error); }); }; return ( <div className='w-full flex justify-between flex-col'> <h1 className='text-white text-[40px]'>Data from back end test</h1> <ul> {data.map(item => ( <li className='text-secondary' key={item.id}>{item.Description}</li> ))} </ul> <div> {images && images.map(img => ( <div className='w-full flex' key={img.id}> <div> <img className='flex h-64 w-64' src={`data:image/jpeg;base64,${img.base64}`} alt={`Product ${img.id}`} /> </div> </div> ))} </div> </div> ); }; export default Product;
内容的提问来源于stack exchange,提问作者Quachu
相关产品推荐
相关产品推荐

