在Next.js中使用@google/generative-AI通过URL上传视频的问题
在Next.js中通过URL上传视频到Google Gemini的解决方案
问题原因
GoogleAIFileManager.uploadFile()方法的第一个参数仅支持本地文件路径,不直接接受远程URL。你传入Vercel Blob的视频URL后,系统会将其解析为本地文件路径(比如错误信息里的C:\Users\...\https:\...mp4),导致找不到文件报错。
解决方案
需要先把远程视频下载到本地临时文件,再将临时文件上传到Google AI平台,完成推理后清理临时文件和云端文件。具体步骤:
- 下载远程视频到Buffer
- 创建本地临时文件存储视频内容
- 上传临时文件到GoogleAIFileManager
- 等待文件处理完成后调用模型生成摘要
- 清理本地临时文件和云端文件
修改后的完整代码
"use server" const { GoogleGenerativeAI } = require("@google/generative-ai"); import { GoogleAIFileManager, FileState } from "@google/generative-ai/server"; import { redirect } from "next/navigation"; import fetchVideoById from "./fetchVideoById"; import { writeFile, unlink } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import crypto from "node:crypto"; // Initialize GoogleAIFileManager with your API_KEY. const fileManager = new GoogleAIFileManager(process.env.API_KEY); // Access your API key as an environment variable const genAI = new GoogleGenerativeAI(process.env.API_KEY); // Choose a Gemini model. const model = genAI.getGenerativeModel({ model: "gemini-1.5-pro", }); export async function generateSummary(formData) { const rows = await fetchVideoById(formData.get("id")); const url = rows["url"]; let tempFilePath = null; let cloudFileName = null; try { console.log("Downloading remote video..."); // 1. 下载远程视频到Buffer const response = await fetch(url); if (!response.ok) throw new Error(`Failed to download video: ${response.statusText}`); const videoBuffer = await response.arrayBuffer(); // 2. 创建临时文件 const tempFileName = `${crypto.randomUUID()}.mp4`; tempFilePath = join(tmpdir(), tempFileName); await writeFile(tempFilePath, Buffer.from(videoBuffer)); console.log("Uploading file to Google AI..."); // 3. 上传临时文件到GoogleAIFileManager const uploadResponse = await fileManager.uploadFile(tempFilePath, { mimeType: "video/mp4", displayName: rows["title"], }); cloudFileName = uploadResponse.file.name; console.log(`Uploaded file ${uploadResponse.file.displayName} as: ${uploadResponse.file.uri}`); // 4. 等待文件处理完成 let file = await fileManager.getFile(cloudFileName); while (file.state === FileState.PROCESSING) { process.stdout.write("."); await new Promise(resolve => setTimeout(resolve, 10000)); // 等待10秒再轮询 file = await fileManager.getFile(cloudFileName); } if (file.state === FileState.FAILED) { throw new Error("Video processing failed."); } console.log(`File ${file.displayName} is ready for inference as ${file.uri}`); // 5. 调用模型生成摘要 const result = await model.generateContent([ { fileData: { mimeType: uploadResponse.file.mimeType, fileUri: uploadResponse.file.uri } }, { text: "Summarize this video." }, ]); const summary = result.response.text(); console.log(summary); return summary; } catch (error) { console.error("Error during summary generation:", error); throw error; } finally { // 6. 清理资源:删除本地临时文件和云端文件 if (tempFilePath) { try { await unlink(tempFilePath); console.log("Deleted local temp file."); } catch (err) { console.error("Failed to delete temp file:", err); } } if (cloudFileName) { try { await fileManager.deleteFile(cloudFileName); console.log("Deleted cloud file."); } catch (err) { console.error("Failed to delete cloud file:", err); } } } }
关键说明
- 使用
node:os/tmpdir()获取系统临时目录,避免权限问题 - 用
crypto.randomUUID()生成唯一临时文件名,防止冲突 - 用
finally块确保无论成功失败都会清理临时文件和云端文件 - 增加了下载视频的错误处理,确保下载失败时能及时抛出错误
内容的提问来源于stack exchange,提问作者kool
相关产品推荐
相关产品推荐

