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

从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编码而非图片的二进制数据,最终图片无法正确渲染,仅显示黑色占位。

修复步骤

  1. 新增工具函数,将十六进制字符串转换为Uint8Array格式的二进制数据
  2. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 03:20:52