如何通过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字符串:
- 在Hasura控制台为音频表添加计算字段,SQL表达式为
encode(audio_data, 'base64'),命名为audio_base64 - 读取时转换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
相关产品推荐
相关产品推荐

