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

如何通过Hasura正确存储与读取bytea格式音频数据?

解决Hasura中bytea存储/读取音频Blob无法播放的问题

核心问题

PostgreSQL的bytea字段存储二进制数据时需严格匹配编码格式;Hasura通过GraphQL API传输bytea时,默认以无\x前缀的hex编码字符串作为输入输出格式。之前的转换失败多源于编码格式不匹配或MIME类型丢失。

正确存储流程

将音频Blob转换为Uint8Array后转成无前缀的hex字符串,作为bytea字段值传入Hasura mutation:

async function saveAudioBlob(blob) {
  // Blob转ArrayBuffer
  const arrayBuffer = await blob.arrayBuffer();
  // 转Uint8Array
  const uint8Arr = new Uint8Array(arrayBuffer);
  // 转hex字符串(每个字节补两位,避免单字符十六进制出错)
  const hexStr = Array.from(uint8Arr)
    .map(byte => byte.toString(16).padStart(2, '0'))
    .join('');
  
  // 调用Hasura mutation
  await fetch('/v1/graphql', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: `
        mutation SaveAudio($audioData: bytea!) {
          insert_audio_one(object: { audio_data: $audioData }) {
            id
          }
        }
      `,
      variables: { audioData: hexStr }
    })
  });
}

正确读取流程

从Hasura获取hex格式的bytea字符串,转换回Uint8Array后创建Blob(必须指定原始音频的MIME类型):

async function getAudioBlob(audioId) {
  // 查询Hasura获取hex格式数据
  const res = await fetch('/v1/graphql', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: `
        query GetAudio($id: uuid!) {
          audio_by_pk(id: $id) {
            audio_data
          }
        }
      `,
      variables: { id: audioId }
    })
  });
  const data = await res.json();
  const hexStr = data.data.audio_by_pk.audio_data;

  // hex字符串转Uint8Array
  const uint8Arr = new Uint8Array(
    hexStr.match(/.{1,2}/g).map(byte => parseInt(byte, 16))
  );
  
  // 创建Blob(替换为你的音频MIME类型,如audio/mpeg、audio/wav)
  return new Blob([uint8Arr], { type: 'audio/mpeg' });
}

替代方案:用计算字段直接返回Base64

若不想手动处理hex转换,可在Hasura中创建计算字段,直接将bytea转为base64字符串:

  1. 在Hasura控制台为音频表添加计算字段,SQL表达式为encode(audio_data, 'base64'),命名为audio_base64
  2. 读取时转换Blob:
async function getAudioBlobFromBase64(audioId) {
  const res = await fetch('/v1/graphql', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: `
        query GetAudio($id: uuid!) {
          audio_by_pk(id: $id) {
            audio_base64
          }
        }
      `,
      variables: { id: audioId }
    })
  });
  const data = await res.json();
  const base64Str = data.data.audio_by_pk.audio_base64;
  
  // Base64转Blob
  const byteCharacters = atob(base64Str);
  const byteNumbers = new Array(byteCharacters.length);
  for (let i = 0; i < byteCharacters.length; i++) {
    byteNumbers[i] = byteCharacters.charCodeAt(i);
  }
  const byteArray = new Uint8Array(byteNumbers);
  return new Blob([byteArray], { type: 'audio/mpeg' });
}

关键注意事项

  • 必须保留原始音频的MIME类型:创建Blob时指定的类型需和原始Blob一致,否则浏览器无法识别播放。
  • 不要修改PostgreSQL的bytea_output设置:默认值为hex,若改为escape需调整转换逻辑,不推荐修改。
  • 禁止直接将Base64字符串传入bytea字段:会导致编码错误,需先转hex或通过计算字段处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 05:40:42