Flutter使用dart_openai发送Base64图片调用OpenAI API时触发400错误的排查与解决咨询
Flutter使用dart_openai发送Base64图片调用OpenAI API时触发400错误的排查与解决咨询
我最近在开发Flutter聊天应用的图片交互功能,用的是dart_openai: ^5.1.0包调用OpenAI的GPT-4 Turbo模型,但在发送本地图片时遇到了400 Bad Request错误。
我的思路是把本地图片路径转成字节数组,压缩后编码成Base64,再拼接成data:image/jpeg;base64,...格式的字符串传入OpenAIChatCompletionChoiceMessageContentItemModel.imageUrl()方法,但API一直返回错误。
我的图片处理与消息构建代码:
if (m.imageUrls.isNotEmpty) { for (final imagePath in m.imageUrls) { print('processing image...'); print('image path $imagePath'); try { final compressedBytes = await compressImage(imagePath); final base64Image = base64Encode(compressedBytes); contentItems.add( OpenAIChatCompletionChoiceMessageContentItemModel.imageUrl( 'data:image/jpeg;base64,$base64Image', ), ); } catch (e) { print('Error processing image: $e'); } } }
图片压缩函数:
Future<Uint8List> compressImage(String imagePath) async { final imageFile = File(imagePath); final bytes = await imageFile.readAsBytes(); // Decode image final image = img.decodeImage(bytes)!; // Resize to maximum 1024px width (maintain aspect ratio) final resized = img.copyResize(image, width: 1024); // Compress with 80% quality return Uint8List.fromList(img.encodeJpg(resized, quality: 80)); }
完整的OpenAIService类代码:
import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:dart_openai/dart_openai.dart'; import 'package:flutter_template/features/chat/models/chat_model.dart'; import 'package:path_provider/path_provider.dart'; import 'package:image/image.dart' as img; Future<Uint8List> compressImage(String imagePath) async { final imageFile = File(imagePath); final bytes = await imageFile.readAsBytes(); // Decode image final image = img.decodeImage(bytes)!; // Resize to maximum 1024px width (maintain aspect ratio) final resized = img.copyResize(image, width: 1024); // Compress with 80% quality return Uint8List.fromList(img.encodeJpg(resized, quality: 80)); } class OpenAIService { OpenAIService(String apiKey) { OpenAI.apiKey = apiKey; } // New method to transcribe audio Future<String> transcribeAudio(File audioFile) async { final transcription = await OpenAI.instance.audio.createTranscription( file: audioFile, model: 'whisper-1', ); return transcription.text; } Stream<String> streamChatCompletion(List<ChatMessage> messages) async* { final recentMessages = messages.length > 10 ? messages.sublist(messages.length - 10) : messages; // Convert ChatMessages to OpenAI messages final openaiMessages = await Future.wait(recentMessages.map((m) async { final contentItems = <OpenAIChatCompletionChoiceMessageContentItemModel>[]; // Handle text content if (m.content.isNotEmpty) { contentItems.add( OpenAIChatCompletionChoiceMessageContentItemModel.text(m.content), ); } // Handle images (convert to base64) if (m.imageUrls.isNotEmpty) { for (final imagePath in m.imageUrls) { print('processing image...'); print('image path $imagePath'); try { final compressedBytes = await compressImage(imagePath); final base64Image = base64Encode(compressedBytes); contentItems.add( OpenAIChatCompletionChoiceMessageContentItemModel.imageUrl( 'data:image/jpeg;base64,$base64Image', ), ); } catch (e) { print('Error processing image: $e'); } } } // Handle audio (transcribe to text) if (m.audioBytes != null) { print('transcribing audio...'); // Create temp file for audio final tempDir = await getTemporaryDirectory(); final tempFile = File('${tempDir.path}/audio_temp.wav') ..writeAsBytesSync(m.audioBytes!); final transcription = await transcribeAudio(tempFile); print('audio transcribed: $transcription'); contentItems.add( OpenAIChatCompletionChoiceMessageContentItemModel.text( '[Audio transcription]: $transcription'), ); } return OpenAIChatCompletionChoiceMessageModel( content: contentItems, role: m.isUser ? OpenAIChatMessageRole.user : OpenAIChatMessageRole.assistant, ); })); // Determine model based on content final hasImages = messages.any((m) => m.imageUrls.isNotEmpty); final model = hasImages ? 'gpt-4-turbo' : 'gpt-3.5-turbo'; final stream = OpenAI.instance.chat.createStream( model: model, messages: openaiMessages, ); // Process stream await for (final chunk in stream) { final content = chunk.choices.first.delta.content; if (content != null) { // Combine all text content final textContent = content .where((item) => item?.type == 'text') .map((item) => item?.text) .join(); if (textContent.isNotEmpty) { yield textContent; } } } } }
我的疑问:
- 是不是
dart_openai的imageUrl()方法只支持公网可访问的HTTPS URL,而不支持Base64的data URI? - 如果确实不支持,那是不是必须先把图片上传到云存储(比如Firebase Storage)拿到公网URL再传入?
- 有没有可能是我的Base64格式或者图片压缩处理有问题,导致API无法解析?
希望有经验的开发者能帮我排查下问题,或者给出可行的本地图片直接发送方案。
内容来源于stack exchange
相关产品推荐
相关产品推荐

