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

Flutter上传音频至Gemini API遇URI错误求解决方案

Flutter调用Gemini API分析音频的URI错误解决

问题场景

在Flutter中开发音频分析功能,选择音频文件上传到Gemini API以识别音频流派和标签,但在FilePart(uri: fileId)处提示uri未定义,尝试多种方法无法解决,不确定是否和模型名称有关。

错误原因

  1. 参数类型不匹配:FilePart的uri参数要求传入Uri类型对象,但代码中直接传入了字符串类型的fileId
  2. 上传端点错误:当前使用的generateContent是生成内容的接口,并非文件上传的专用端点
  3. 文件标识处理错误:上传得到的文件标识需要构造成Gemini可识别的完整Uri格式才能使用

修复方案

1. 修正文件上传接口

将上传文件的端点替换为Gemini的文件上传专用接口:

final uri = Uri.parse('https://generativelanguage.googleapis.com/v1beta/files?key=$apiKey');

2. 构造正确的FilePart Uri

上传成功后,用返回的文件标识构造符合要求的Uri:

FilePart(uri: Uri.parse("https://generativelanguage.googleapis.com/v1beta/files/$fileId"))

3. 优化分析逻辑

原提问同时涉及音乐和图片分析,但仅上传了音频,调整提问内容聚焦音频分析;同时简化对话逻辑,直接使用generateContent方法更高效。

完整修正代码

import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:http/http.dart' as http;
import 'package:google_generative_ai/google_generative_ai.dart';

void main() {
  runApp(const MusicAnalyzerApp());
}

class MusicAnalyzerApp extends StatelessWidget {
  const MusicAnalyzerApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Gemini Music Analyzer',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MusicAnalyzerHome(),
    );
  }
}

class MusicAnalyzerHome extends StatefulWidget {
  @override
  _MusicAnalyzerHomeState createState() => _MusicAnalyzerHomeState();
}

class _MusicAnalyzerHomeState extends State<MusicAnalyzerHome> {
  String? _filePath;
  String? _musicAnalysis;
  bool _isLoading = false;

  Future<void> _pickFile() async {
    FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.audio);

    if (result != null) {
      setState(() {
        _filePath = result.files.single.path;
      });
    }
  }

  Future<void> _analyzeMusic() async {
    if (_filePath == null) return;

    setState(() {
      _isLoading = true;
      _musicAnalysis = null;
    });

    const apiKey = '你的API_KEY'; // 替换为自己的Gemini API密钥

    final fileId = await uploadFileToGemini(apiKey, _filePath!);
    if (fileId == null) {
      setState(() {
        _isLoading = false;
      });
      _showErrorDialog('文件上传失败');
      return;
    }

    final response = await analyzeMusicWithGemini(apiKey, fileId);

    setState(() {
      _isLoading = false;
      _musicAnalysis = response;
    });
  }

  Future<String?> uploadFileToGemini(String apiKey, String filePath) async {
    // 使用Gemini专用文件上传接口
    final uri = Uri.parse('https://generativelanguage.googleapis.com/v1beta/files?key=$apiKey');

    final request = http.MultipartRequest('POST', uri)
      ..headers['Authorization'] = 'Bearer $apiKey'
      ..fields['purpose'] = 'GENERATE_CONTENT' // 指定文件用途
      ..files.add(await http.MultipartFile.fromPath('file', filePath));

    final response = await request.send();

    if (response.statusCode == 200) {
      final responseBody = await response.stream.bytesToString();
      final jsonResponse = jsonDecode(responseBody);
      return jsonResponse['name']; // 上传成功后返回的是name字段,不是fileId
    } else {
      final errorBody = await response.stream.bytesToString();
      debugPrint('上传错误:$errorBody');
      return null;
    }
  }

  Future<String?> analyzeMusicWithGemini(String apiKey, String fileId) async {
    final model = GenerativeModel(
      model: 'gemini-1.5-pro',
      apiKey: apiKey,
      generationConfig: GenerationConfig(
        temperature: 0.7, // 调整温度让结果更稳定
        topK: 64,
        topP: 0.95,
        maxOutputTokens: 8192,
        responseMimeType: 'text/plain',
      ),
    );

    // 构造正确的FilePart,使用Uri类型
    final content = Content.multi([
      FilePart(uri: Uri.parse("https://generativelanguage.googleapis.com/v1beta/files/$fileId")),
      const TextPart('请分析这个音频的流派和相关标签,给出详细的结果'),
    ]);

    final response = await model.generateContent(content);
    return response.text;
  }

  void _showErrorDialog(String message) {
    showDialog<void>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('错误'),
          content: Text(message),
          actions: <Widget>[
            TextButton(
              child: const Text('确定'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Gemini音频分析工具'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: <Widget>[
            ElevatedButton(
              onPressed: _pickFile,
              child: const Text('选择音频文件'),
            ),
            const SizedBox(height: 16),
            if (_filePath != null)
              Text('已选择文件:${_filePath!.split('/').last}'),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _analyzeMusic,
              child: const Text('开始分析'),
            ),
            const SizedBox(height: 16),
            if (_isLoading)
              const CircularProgressIndicator(),
            if (_musicAnalysis != null)
              Expanded(
                child: SingleChildScrollView(
                  child: Text(_musicAnalysis!),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

关键注意点

  • 替换代码中的你的API_KEY为自己的Gemini API密钥
  • 确保上传的音频文件大小不超过Gemini的限制(当前gemini-1.5-pro支持最大2GB的音频文件)
  • 上传文件时必须指定purpose为GENERATE_CONTENT,否则文件无法用于内容生成
  • 上传成功后返回的name字段就是文件的唯一标识,需要用它构造完整Uri

内容的提问来源于stack exchange,提问作者Vikas Hansdak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:34:51