GEMINI_FILE_API.md 13 KB

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),一次性上传。

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:初始化上传会话

# 自动获取文件信息
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:上传文件内容

# 提取上传 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}"

完整脚本示例

#!/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. 列出文件

curl "http://your-api.com/v1beta/files?key=YOUR_API_KEY"

响应示例

{
  "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 请求中引用该文件:

#!/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 .

完整的上传和使用工作流

#!/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

    -H "Authorization: Bearer YOUR_API_KEY"
    
    1. x-goog-api-key Header bash -H "x-goog-api-key: YOUR_API_KEY"
  2. Query Parameter

    ?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 或环境变量中配置:

    environment:
    - MAX_REQUEST_BODY_MB=500  # 支持最大 500MB 文件
    

或在启动时设置环境变量:

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 方式

    # 使用 Resumable Upload,自动设置正确的 MIME 类型
    MIME_TYPE=$(file -b --mime-type "${FILE_PATH}")
    # ... 按照 resumable upload 步骤上传
    
    1. 或者在 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,而不是猜测:

# 从文件列表获取正确的 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)

    • 初始版本
    • 完全独立的模块架构
    • 支持文件上传和列表操作
    • 多种认证方式支持
    • 自动渠道选择