Flutter上传音频至Gemini API遇URI错误求解决方案
Flutter调用Gemini API分析音频的URI错误解决
问题场景
在Flutter中开发音频分析功能,选择音频文件上传到Gemini API以识别音频流派和标签,但在FilePart(uri: fileId)处提示uri未定义,尝试多种方法无法解决,不确定是否和模型名称有关。
错误原因
- 参数类型不匹配:
FilePart的uri参数要求传入Uri类型对象,但代码中直接传入了字符串类型的fileId - 上传端点错误:当前使用的
generateContent是生成内容的接口,并非文件上传的专用端点 - 文件标识处理错误:上传得到的文件标识需要构造成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
相关产品推荐
相关产品推荐

