# Gemini File API 独立模块 ## 概述 Gemini File API 是一个完全独立的模块,与其他 API 逻辑完全隔离,不会相互干扰。 **支持的功能**: - ✅ 文件上传 (POST /upload/v1beta/files) - ✅ 文件列表 (GET /v1beta/files) **注意**:本模块仅实现了文件上传和列表功能,这是使用 Gemini File API 的核心功能。如需获取单个文件信息或删除文件,可以通过列表接口获取所有文件信息。 ## 架构设计 ### 1. 独立的路由模块 - **文件**: `router/gemini_file_router.go` - **功能**: 专门处理 Gemini 文件操作的路由 - **路径**: - `GET /v1beta/files` - 列出文件 - `POST /upload/v1beta/files` - 上传文件 ### 2. 独立的认证中间件 - **文件**: `middleware/gemini_file_auth.go` - **功能**: - 专门为 Gemini File API 设计的认证逻辑 - 支持多种认证方式(Authorization header, x-goog-api-key, query parameter) - 自动查找可用的 Gemini 渠道 - 不依赖其他中间件 ### 3. 独立的控制器 - **文件**: `controller/relay_gemini_file.go` - **功能**: 处理所有文件操作的业务逻辑 - **方法**: - `RelayGeminiFileList` - 列出文件 - `RelayGeminiFileUpload` - 上传文件 ### 4. 独立的辅助函数 - **文件**: `relay/channel/gemini/file_helper.go` - **功能**: - URL 构建 - 请求转发 - Multipart 表单处理 ## 使用方式 ### 1. 上传文件 本模块支持两种上传方式: #### 方式 1:简单上传(Multipart Upload) 适合小文件(< 100MB),一次性上传。 ```bash curl -X POST "http://your-api.com/upload/v1beta/files?key=YOUR_API_KEY" \ -F "file=@/path/to/file.pdf" \ -F "display_name=My Document" ``` **注意**:此方式可能无法正确设置 MIME 类型,文件会被标记为 `application/octet-stream`。 #### 方式 2:可恢复上传(Resumable Upload)⭐ 推荐 适合大文件或需要正确 MIME 类型的场景。分为两步: **步骤 1:初始化上传会话** ```bash # 自动获取文件信息 VIDEO_PATH="/path/to/video.mp4" MIME_TYPE=$(file -b --mime-type "${VIDEO_PATH}") NUM_BYTES=$(wc -c < "${VIDEO_PATH}") # 初始化上传 curl "http://your-api.com/upload/v1beta/files?key=YOUR_API_KEY" \ -D upload-header.tmp \ -H "X-Goog-Upload-Protocol: resumable" \ -H "X-Goog-Upload-Command: start" \ -H "X-Goog-Upload-Header-Content-Length: ${NUM_BYTES}" \ -H "X-Goog-Upload-Header-Content-Type: ${MIME_TYPE}" \ -H "Content-Type: application/json" \ -d '{"file": {"display_name": "My Video"}}' ``` **步骤 2:上传文件内容** ```bash # 提取上传 URL upload_url=$(grep -i "x-goog-upload-url: " upload-header.tmp | cut -d" " -f2 | tr -d "\r") # 上传文件 curl "${upload_url}" \ -H "Content-Length: ${NUM_BYTES}" \ -H "X-Goog-Upload-Offset: 0" \ -H "X-Goog-Upload-Command: upload, finalize" \ --data-binary "@${VIDEO_PATH}" ``` **完整脚本示例**: ```bash #!/bin/bash API_KEY="YOUR_API_KEY" FILE_PATH="/path/to/video.mp4" DISPLAY_NAME="My Video" # 获取文件信息 MIME_TYPE=$(file -b --mime-type "${FILE_PATH}") NUM_BYTES=$(wc -c < "${FILE_PATH}") echo "Uploading: ${FILE_PATH}" echo "MIME Type: ${MIME_TYPE}" echo "Size: ${NUM_BYTES} bytes" # 步骤 1: 初始化上传 curl "http://your-api.com/upload/v1beta/files?key=${API_KEY}" \ -D upload-header.tmp \ -H "X-Goog-Upload-Protocol: resumable" \ -H "X-Goog-Upload-Command: start" \ -H "X-Goog-Upload-Header-Content-Length: ${NUM_BYTES}" \ -H "X-Goog-Upload-Header-Content-Type: ${MIME_TYPE}" \ -H "Content-Type: application/json" \ -d "{\"file\": {\"display_name\": \"${DISPLAY_NAME}\"}}" # 步骤 2: 提取上传 URL upload_url=$(grep -i "x-goog-upload-url: " upload-header.tmp | cut -d" " -f2 | tr -d "\r") if [ -z "$upload_url" ]; then echo "Error: Failed to get upload URL" exit 1 fi echo "Upload URL: ${upload_url}" # 步骤 3: 上传文件 curl "${upload_url}" \ -H "Content-Length: ${NUM_BYTES}" \ -H "X-Goog-Upload-Offset: 0" \ -H "X-Goog-Upload-Command: upload, finalize" \ --data-binary "@${FILE_PATH}" | jq . # 清理 rm -f upload-header.tmp ``` ### 2. 列出文件 ```bash curl "http://your-api.com/v1beta/files?key=YOUR_API_KEY" ``` **响应示例**: ```json { "files": [ { "name": "files/abc-123", "displayName": "My Document", "mimeType": "video/mp4", "sizeBytes": "12345", "createTime": "2024-01-20T10:00:00Z", "updateTime": "2024-01-20T10:00:00Z", "expirationTime": "2024-01-22T10:00:00Z", "uri": "https://generativelanguage.googleapis.com/v1beta/files/abc-123", "state": "ACTIVE" } ] } ``` ### 3. 使用上传的文件生成内容 上传文件后,可以在 Gemini API 的 `generateContent` 请求中引用该文件: ```bash #!/bin/bash API_KEY="YOUR_API_KEY" # 从文件列表中获取文件信息 FILE_INFO=$(curl -s "http://your-api.com/v1beta/files?key=${API_KEY}" | jq -r '.files[0]') FILE_NAME=$(echo "$FILE_INFO" | jq -r '.name') MIME_TYPE=$(echo "$FILE_INFO" | jq -r '.mimeType') echo "Using file: $FILE_NAME" echo "MIME type: $MIME_TYPE" # 使用文件生成内容 curl "http://your-api.com/v1beta/models/gemini-2.0-flash-exp:generateContent?key=${API_KEY}" \ -H 'Content-Type: application/json' \ -X POST \ -d '{ "contents": [{ "parts":[ {"fileData":{"mimeType": "'"${MIME_TYPE}"'", "fileUri": "'"${FILE_NAME}"'"}}, {"text": "请总结这个视频的内容"} ] }] }' | jq . ``` **完整的上传和使用工作流**: ```bash #!/bin/bash API_KEY="YOUR_API_KEY" VIDEO_PATH="/path/to/video.mp4" # ========== 第一步:上传视频文件 ========== echo "=== Step 1: Uploading video ===" MIME_TYPE=$(file -b --mime-type "${VIDEO_PATH}") NUM_BYTES=$(wc -c < "${VIDEO_PATH}") # 初始化上传 curl "http://your-api.com/upload/v1beta/files?key=${API_KEY}" \ -D upload-header.tmp \ -H "X-Goog-Upload-Protocol: resumable" \ -H "X-Goog-Upload-Command: start" \ -H "X-Goog-Upload-Header-Content-Length: ${NUM_BYTES}" \ -H "X-Goog-Upload-Header-Content-Type: ${MIME_TYPE}" \ -H "Content-Type: application/json" \ -d '{"file": {"display_name": "My Video"}}' # 提取上传 URL upload_url=$(grep -i "x-goog-upload-url: " upload-header.tmp | cut -d" " -f2 | tr -d "\r") # 上传文件 UPLOAD_RESPONSE=$(curl -s "${upload_url}" \ -H "Content-Length: ${NUM_BYTES}" \ -H "X-Goog-Upload-Offset: 0" \ -H "X-Goog-Upload-Command: upload, finalize" \ --data-binary "@${VIDEO_PATH}") echo "$UPLOAD_RESPONSE" | jq . # ========== 第二步:提取文件信息 ========== FILE_NAME=$(echo "$UPLOAD_RESPONSE" | jq -r '.file.name') FILE_MIME=$(echo "$UPLOAD_RESPONSE" | jq -r '.file.mimeType') echo "" echo "=== Step 2: File uploaded ===" echo "File name: $FILE_NAME" echo "MIME type: $FILE_MIME" # ========== 第三步:使用文件生成内容 ========== echo "" echo "=== Step 3: Generating content ===" curl "http://your-api.com/v1beta/models/gemini-2.0-flash-exp:generateContent?key=${API_KEY}" \ -H 'Content-Type: application/json' \ -X POST \ -d '{ "contents": [{ "parts":[ {"fileData":{"mimeType": "'"${FILE_MIME}"'", "fileUri": "'"${FILE_NAME}"'"}}, {"text": "Summarize this video and create a quiz based on it."} ] }] }' | jq . # 清理 rm -f upload-header.tmp ``` ## 两种上传方式对比 | 特性 | 简单上传 (Multipart) | 可恢复上传 (Resumable) | |------|---------------------|----------------------| | **MIME 类型** | ❌ 通常为 `application/octet-stream` | ✅ 正确识别(如 `video/mp4`) | | **文件大小限制** | 适合 < 100MB | ✅ 支持大文件 (< 500MB) | | **上传步骤** | 一步完成 | 两步(初始化 + 上传) | | **网络容错** | ❌ 失败需重新上传 | ✅ 支持断点续传(理论上) | | **使用场景** | 小文件快速上传 | 视频、音频等大文件 | | **推荐度** | ⭐⭐ | ⭐⭐⭐⭐⭐ | **建议**: - 📹 **视频文件**:必须使用 Resumable Upload - 🎵 **音频文件**:推荐使用 Resumable Upload - 📄 **文档文件**:两种方式都可以 - 🖼️ **图片文件**:两种方式都可以 ## 认证方式 支持以下任意一种认证方式: 1. **Authorization Header** ```bash -H "Authorization: Bearer YOUR_API_KEY" ``` 2. **x-goog-api-key Header** ```bash -H "x-goog-api-key: YOUR_API_KEY" ``` 3. **Query Parameter** ```bash ?key=YOUR_API_KEY ``` ## 特性 ### ✅ 完全隔离 - 独立的路由设置 - 独立的认证逻辑 - 独立的中间件链 - 不影响其他 API 功能 ### ✅ 两种上传方式 - **简单上传 (Multipart)**:适合小文件,一步完成 - **可恢复上传 (Resumable)**:适合大文件,正确设置 MIME 类型 ### ✅ 正确的 MIME 类型支持 - 自动识别视频文件(video/mp4, video/quicktime 等) - 自动识别音频文件(audio/mpeg, audio/wav 等) - 支持文档、图片等各种文件类型 - 确保 Gemini 能正确处理文件 ### ✅ 自动渠道选择 - 自动查找可用的 Gemini 渠道 - 支持多个 Gemini 模型作为备选 - 失败自动重试其他渠道 ### ✅ 多种认证方式 - 灵活的认证方式支持 - 兼容 OpenAI、Gemini、Claude 等多种 API 风格 - 支持 Authorization header、x-goog-api-key、query parameter ### ✅ 大文件支持 - 支持最大 500MB 的文件上传 - 流式传输,内存占用低 - 适合视频、音频等大型媒体文件 ## 配置 ### 环境变量配置(可选) 如果需要支持大文件上传(超过默认限制),可以在 `docker-compose.yml` 或环境变量中配置: ```yaml environment: - MAX_REQUEST_BODY_MB=500 # 支持最大 500MB 文件 ``` 或在启动时设置环境变量: ```bash export MAX_REQUEST_BODY_MB=500 ``` ## 注意事项 1. **文件过期**: 上传的文件会在 48 小时后自动过期 2. **API Key 权限**: 确保使用的 API Key 有访问 Gemini File API 的权限 3. **渠道配置**: 需要在系统中配置至少一个 Gemini 渠道 4. **支持的模型**: - gemini-2.0-flash - gemini-1.5-flash - gemini-1.5-pro - gemini-2.0-flash-exp - gemini-pro - gemini-1.0-pro ## 故障排除 ### 问题: "No available Gemini channel" **解决方案**: 1. 检查是否配置了 Gemini 渠道 2. 确认渠道状态为启用 3. 验证渠道支持的模型列表 ### 问题: "API key is required" **解决方案**: 1. 确保请求中包含 API Key 2. 检查 API Key 格式是否正确 3. 验证 API Key 是否有效 ### 问题: 文件上传失败 **解决方案**: 1. 检查文件大小是否超过限制 2. 确认 `MAX_REQUEST_BODY_MB` 配置 3. 验证 Gemini API Key 权限 ### 问题: 文件 MIME 类型不正确(显示为 application/octet-stream) **原因**:使用了简单上传 (Multipart) 方式 **解决方案**: 1. **推荐**:改用 Resumable Upload 方式 ```bash # 使用 Resumable Upload,自动设置正确的 MIME 类型 MIME_TYPE=$(file -b --mime-type "${FILE_PATH}") # ... 按照 resumable upload 步骤上传 ``` 2. 或者在 multipart upload 时指定类型: ```bash curl -X POST "http://your-api.com/upload/v1beta/files?key=YOUR_API_KEY" \ -F "file=@video.mp4;type=video/mp4" \ -F "display_name=My Video" ``` ### 问题: 使用文件时报错 "expected application/octet-stream" **原因**:上传时的 MIME 类型与使用时不匹配 **解决方案**: 使用文件时,必须使用文件列表中返回的 `mimeType`,而不是猜测: ```bash # 从文件列表获取正确的 MIME 类型 FILE_INFO=$(curl -s "http://your-api.com/v1beta/files?key=YOUR_API_KEY" | jq -r '.files[0]') MIME_TYPE=$(echo "$FILE_INFO" | jq -r '.mimeType') # 使用获取到的 MIME 类型 curl "http://your-api.com/v1beta/models/gemini-2.0-flash-exp:generateContent?key=YOUR_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "contents": [{ "parts":[ {"fileData":{"mimeType": "'"${MIME_TYPE}"'", "fileUri": "files/abc-123"}} ] }] }' ``` ## 开发指南 ### 添加新的文件操作 1. 在 `controller/relay_gemini_file.go` 中添加新的处理函数 2. 在 `router/gemini_file_router.go` 中注册新的路由 3. 如需特殊逻辑,在 `relay/channel/gemini/file_helper.go` 中添加辅助函数 ### 修改认证逻辑 编辑 `middleware/gemini_file_auth.go` 中的 `GeminiFileAuth` 函数。 ### 自定义渠道选择 修改 `middleware/gemini_file_auth.go` 中的 `findGeminiFileChannel` 函数。 ## 版本历史 - **v1.1.0** (2026-01-20) - ✅ 新增支持 Resumable Upload Protocol - ✅ 正确设置文件 MIME 类型(video/mp4、audio/mpeg 等) - ✅ 支持两种上传方式:简单上传和可恢复上传 - ✅ 保持向后兼容 - ✅ 完善文档和使用示例 - **v1.0.0** (2026-01-20) - 初始版本 - 完全独立的模块架构 - 支持文件上传和列表操作 - 多种认证方式支持 - 自动渠道选择