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

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;
        }
      }
    }
  }
}

我的疑问:

  1. 是不是dart_openai的imageUrl()方法只支持公网可访问的HTTPS URL,而不支持Base64的data URI?
  2. 如果确实不支持,那是不是必须先把图片上传到云存储(比如Firebase Storage)拿到公网URL再传入?
  3. 有没有可能是我的Base64格式或者图片压缩处理有问题,导致API无法解析?

希望有经验的开发者能帮我排查下问题,或者给出可行的本地图片直接发送方案。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 10:44:34