fix: 修复 Gemini 图片无法上传问题

This commit is contained in:
foxhui
2025-12-13 00:16:57 +08:00
Unverified
parent dffb209dac
commit 002c607da0
2 changed files with 82 additions and 13 deletions
+12 -13
View File
@@ -2,7 +2,7 @@ import { initBrowserBase } from '../../browser/launcher.js';
import {
sleep,
safeClick,
pasteImages
uploadFilesViaChooser
} from '../../browser/utils.js';
import {
fillPrompt,
@@ -58,23 +58,22 @@ async function generateImage(context, prompt, imgPaths, modelId, meta = {}) {
await inputLocator.waitFor({ timeout: 30000 });
await sleep(1500, 2500);
// 2. 上传图片
// 2. 上传图片 (使用 filechooser 事件,因为 Firefox 不会创建 DOM input 元素)
if (imgPaths && imgPaths.length > 0) {
const expectedUploads = imgPaths.length;
let uploadedCount = 0;
// 点击加号按钮打开菜单
logger.debug('适配器', '点击加号按钮...', meta);
const uploadMenuBtn = page.getByRole('button', { name: 'Open upload file menu' });
await safeClick(page, uploadMenuBtn, { bias: 'button' });
await sleep(500, 1000);
await pasteImages(page, inputLocator, imgPaths, {
// 使用公共函数上传文件
const uploadFilesBtn = page.getByRole('button', { name: /Upload files/ });
await uploadFilesViaChooser(page, uploadFilesBtn, imgPaths, {
uploadValidator: (response) => {
const url = response.url();
// 检测上传成功:google.com/upload/?upload_id= 的 POST 请求
if (response.status() === 200 &&
return response.status() === 200 &&
url.includes('google.com/upload/') &&
url.includes('upload_id=')) {
uploadedCount++;
logger.info('适配器', `图片上传进度: ${uploadedCount}/${expectedUploads}`, meta);
return uploadedCount >= expectedUploads;
}
return false;
url.includes('upload_id=');
}
});
+70
View File
@@ -404,6 +404,76 @@ export async function pasteImages(page, target, filePaths, options = {}) {
}
}
/**
* 通过 filechooser 事件上传文件 (适用于无 DOM input 元素的场景,如 Firefox)
* @param {import('playwright-core').Page} page - Playwright 页面对象
* @param {string|import('playwright-core').ElementHandle|import('playwright-core').Locator} triggerTarget - 触发文件选择的按钮
* @param {string[]} filePaths - 文件路径数组
* @param {Object} [options] - 可选配置
* @param {Function} [options.uploadValidator] - 自定义上传确认回调函数, 接收 response 参数,返回 true 表示该响应代表一次成功上传
* @param {number} [options.timeout=60000] - 上传超时时间 (毫秒)
* @returns {Promise<void>}
*/
export async function uploadFilesViaChooser(page, triggerTarget, filePaths, options = {}) {
if (!filePaths || filePaths.length === 0) return;
const timeout = options.timeout || 60000;
const expectedUploads = filePaths.length;
let uploadedCount = 0;
logger.info('浏览器', `正在处理 ${filePaths.length} 张图片 (filechooser 模式)...`);
// 设置上传确认监听
const uploadPromise = new Promise((resolve) => {
if (!options.uploadValidator) {
// 无验证器,直接 resolve
resolve();
return;
}
const timeoutId = setTimeout(() => {
cleanup();
logger.warn('浏览器', `图片上传等待超时 (已确认: ${uploadedCount}/${expectedUploads})`);
resolve();
}, timeout);
const onResponse = (response) => {
if (options.uploadValidator(response)) {
uploadedCount++;
logger.info('浏览器', `图片上传进度: ${uploadedCount}/${expectedUploads}`);
if (uploadedCount >= expectedUploads) {
cleanup();
resolve();
}
}
};
const cleanup = () => {
clearTimeout(timeoutId);
page.off('response', onResponse);
};
page.on('response', onResponse);
});
// 设置等待 filechooser 事件(在点击之前)
const fileChooserPromise = page.waitForEvent('filechooser');
// 点击触发按钮
await safeClick(page, triggerTarget, { bias: 'button' });
// 等待 filechooser 事件并设置文件
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(filePaths);
logger.debug('浏览器', '已通过 filechooser 提交文件');
// 等待上传完成(如果有验证器)
if (options.uploadValidator) {
await uploadPromise;
logger.info('浏览器', '所有图片上传完成');
}
}
/**
* 检查页面是否有效
* @param {import('playwright-core').Page} page