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

C# 从PostgreSQL查询结果转换对象为byte[]图片数组长度异常问题

将PostgreSQL查询返回的对象转换为BYTE[]

你拿到的字节数组长度远小于实际值,核心问题是读取逻辑和字段存储类型不匹配,两个现有方案的错误非常明确:

  • 方案1直接强转(byte[])rdr[0]仅拿到13字节,说明你读取到的不是图片二进制本身,是PostgreSQL大对象(Large Object)的OID标识符——OID是32位无符号整数,转成字节后长度仅十多字节,和你拿到的结果完全吻合。
  • 方案2用BinaryFormatter序列化DataRow对象拿到42字节,属于方法用错:BinaryFormatter序列化的是.NET对象的类型元数据+内存数据,输出的是.NET私有序列化格式,和数据库存储的原始图片字节没有任何关系,拿到的几十字节全是序列化头信息,不可能还原出图片。

第一步:先确认字段实际存储类型

先执行以下SQL确认photo_img字段的类型,不同类型读取逻辑完全不同:

SELECT data_type 
FROM information_schema.columns 
WHERE table_name = 'photo' AND column_name = 'photo_img';
  • 如果返回结果是bytea:说明字段直接存储二进制内容,可以直接读取原始字节
  • 如果返回结果是oid:说明字段存的是大对象的引用ID,必须调用大对象接口才能读取完整内容

同时可以执行下面的SQL先确认数据库里存储的内容本身长度是否正确,排除入库阶段的错误:

-- 如果是bytea类型用这个查长度
SELECT length(photo_img) FROM photo WHERE photo_id = 你要查询的图片ID;
-- 如果是oid类型用这个查大对象长度
SELECT pg_column_size(lo_get(photo_img)) FROM photo WHERE photo_id = 你要查询的图片ID;

如果SQL返回的长度不是你预期的675486,说明你之前存图片的逻辑就有问题,先修复入库代码,再调整读取逻辑。


第二步:根据字段类型使用对应读取代码

场景1:字段为bytea类型

不要用多余的转换逻辑,直接通过DataReader的原生方法读取字节,同时把SQL拼接改成参数化查询,避免隐式类型转换错误和SQL注入风险:

using (var conexao = new NpgsqlConnection("你的数据库连接字符串"))
{
    const string sql = @"SELECT p.photo_img 
                        FROM empresa e 
                        INNER JOIN photo p ON e.empresa_img = p.photo_id
                        WHERE e.empresa_img = @imgId";
    using var command = new NpgsqlCommand(sql, conexao);
    command.Parameters.AddWithValue("imgId", id);
    conexao.Open();
    
    using var rdr = command.ExecuteReader();
    if (rdr.Read())
    {
        // 先获取二进制实际长度,再读取完整内容
        int byteLength = (int)rdr.GetBytes(0, 0, null, 0, 0);
        byte[] imageBytes = new byte[byteLength];
        rdr.GetBytes(0, 0, imageBytes, 0, byteLength);
        
        using var imgStream = new MemoryStream(imageBytes);
        // 直接从流加载图片,不需要ImageConverter做中转
        pct_Imagem.Image = Image.FromStream(imgStream);
    }
}

场景2:字段为oid类型(大对象存储)

大对象必须在事务内通过Npgsql提供的大对象管理器读取,不能直接读字段值:

using (var conexao = new NpgsqlConnection("你的数据库连接字符串"))
{
    conexao.Open();
    // 大对象操作要求必须开启事务
    using var transaction = conexao.BeginTransaction();
    const string sql = @"SELECT p.photo_img 
                        FROM empresa e 
                        INNER JOIN photo p ON e.empresa_img = p.photo_id
                        WHERE e.empresa_img = @imgId";
    using var command = new NpgsqlCommand(sql, conexao);
    command.Parameters.AddWithValue("imgId", id);
    
    // 先拿到大对象的OID
    uint imageOid = (uint)command.ExecuteScalar();
    var largeObjectManager = new NpgsqlLargeObjectManager(conexao);
    
    // 打开大对象流,拷贝完整字节
    using var loStream = largeObjectManager.OpenRead(imageOid);
    using var imgStream = new MemoryStream();
    loStream.CopyTo(imgStream);
    
    imgStream.Position = 0;
    pct_Imagem.Image = Image.FromStream(imgStream);
    
    transaction.Commit();
}

避坑提醒

  • 立刻废弃BinaryFormatter转字节的方案,该组件已被微软标记为存在安全风险,且完全不适用于读取数据库原始二进制场景。
  • 所有SQL传值一律用参数化方式,不要直接拼接id变量,否则不仅有注入风险,还可能因为类型不匹配导致读取到错误的内容。
  • 读取到字节后不需要用ImageConverter做转换,直接用Image.FromStream从内存流加载图片即可,多余的转换步骤反而可能引入异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 10:33:22