从Google Drive取Blob转Base64调用OpenAI API时的报错问题
问题
我正尝试通过Chat Completion接口将Google Drive中的文件发送至OpenAI API,已完成认证并可列出、获取目标文件,Google API返回的数据格式为Blob。
问题在于,将Blob转换为Base64编码字符串时,Supabase Edge Function抛出RangeError或DOMException。当前代码片段(已完成认证和files.get操作后):
const fileDownload = await driveApi.files.get({ fileId: 'file_id_string', alt: 'media', }); console.log('file download', fileDownload.data); const blob: Blob = fileDownload.data; const arrayBuffer = await blob.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); // Attempt 1 const charCodeString = String.fromCharCode(...uint8Array); // <-- Fails here const base64String = btoa(charCodeString); // Attempt 2 const decodedString = new TextDecoder().decode(uint8Array); const base64String = btoa(decodedString); // <-- Fails here // Uncomment once I get the base64String right // await openai.chat.completions.create({ // model: 'gpt-4o-mini', // response_format: 'json_object', // messages: [ // { role: 'user', content: [{ url: base64String, type: 'image_url' }] }, // ], // });
两种尝试对应的错误信息:
# Attempt 1 error message RangeError: Maximum call stack size exceeded at Object.handler (file:///repos/supabase-personal/supabase/functions/openai/index.ts:32:38) at Object.runMicrotasks (ext:core/01_core.js:642:26) at processTicksAndRejections (ext:deno_node/_next_tick.ts:39:10) at runNextTicks (ext:deno_node/_next_tick.ts:48:3) at eventLoopTick (ext:core/01_core.js:175:21) at async respond (ext:sb_core_main_js/js/http.js:163:14) # Attempt 2 error message DOMException: The string to be encoded contains characters outside of the Latin1 range. at new DOMException (ext:deno_web/01_dom_exception.js:116:20) at btoa (ext:deno_web/05_base64.js:52:13) at Object.handler (file:///repos/supabase-personal/supabase/functions/openai/index.ts:37:26) at Object.runMicrotasks (ext:core/01_core.js:642:26) at processTicksAndRejections (ext:deno_node/_next_tick.ts:39:10) at runNextTicks (ext:deno_node/_next_tick.ts:48:3) at eventLoopTick (ext:core/01_core.js:175:21) at async respond (ext:sb_core_main_js/js/http.js:163:14)
需要更简便或更优的实现方式将文件转成Base64后发送给OpenAI聊天补全接口。
解决方案
问题原因
- 尝试1中,
String.fromCharCode(...uint8Array)会把Uint8Array的每个元素展开作为参数传递,文件较大时参数数量过多,直接触发调用栈溢出,抛出RangeError。 - 尝试2中,
TextDecoder.decode()将二进制数据解码为UTF-8字符串,但图片等非文本文件的二进制内容包含Latin1范围外的字符,而btoa()仅支持处理Latin1字符,因此抛出DOMException。
正确实现方式
在Supabase Edge Function(基于Deno)环境中,推荐使用以下两种高效方案:
方案1:使用Deno内置Base64编码方法(最优)
const fileDownload = await driveApi.files.get({ fileId: 'file_id_string', alt: 'media', }); const blob: Blob = fileDownload.data; const arrayBuffer = await blob.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); // 直接处理Uint8Array,避免大数组展开 const base64String = Deno.core.encodeBase64(uint8Array); // 拼接OpenAI要求的data URI格式(需替换为实际文件类型,比如从Google Drive的mimeType提取) const imageBase64Url = `data:image/png;base64,${base64String}`; await openai.chat.completions.create({ model: 'gpt-4o-mini', response_format: 'json_object', messages: [ { role: 'user', content: [{ type: 'image_url', image_url: { url: imageBase64Url } }] }, ], });
方案2:分块处理大文件(兼容无Deno.core环境)
如果Deno.core.encodeBase64不可用,可分块处理Uint8Array,避免一次性展开所有元素:
function uint8ArrayToBase64(uint8Array: Uint8Array): string { let base64 = ''; const chunkSize = 0x8000; // 32KB分块,平衡性能与内存 for (let i = 0; i < uint8Array.length; i += chunkSize) { const chunk = uint8Array.subarray(i, i + chunkSize); base64 += btoa(String.fromCharCode(...chunk)); } return base64; } // 调用转换函数 const base64String = uint8ArrayToBase64(uint8Array); // 后续拼接data URI并调用OpenAI API的代码同方案1
关键注意事项
- 必须拼接data URI前缀:OpenAI的
image_url要求格式为data:image/[类型];base64,[编码内容],需根据文件实际类型替换image/png(比如从Google Drive文件的mimeType字段提取)。 - 优先使用Deno内置方法:
Deno.core.encodeBase64直接操作二进制数组,性能远高于分块处理,且不会触发调用栈溢出问题。
内容的提问来源于stack exchange,提问作者Jet
相关产品推荐
相关产品推荐

