|
|
@@ -254,76 +254,95 @@ class AIImageGenerationService
|
|
|
dLog('generate')->info('开始调用GPT-Image2 API', ['task_id' => $task->task_id, 'mode' => $isEditMode ? 'edit' : 'generation']);
|
|
|
logDB('generate', 'info', '开始调用GPT-Image2 API', ['task_id' => $task->task_id, 'mode' => $isEditMode ? 'edit' : 'generation']);
|
|
|
|
|
|
- // 创建专用客户端,设置更长的超时时间(图片生成可能需要较长时间)
|
|
|
+ // 创建专用客户端,优化配置
|
|
|
$gptClient = new Client([
|
|
|
- 'timeout' => 600, // 10分钟超时
|
|
|
- 'connect_timeout' => 30, // 连接超时30秒
|
|
|
- 'read_timeout' => 600, // 读取超时10分钟
|
|
|
- 'http_errors' => false, // 不自动抛出HTTP错误,手动处理
|
|
|
+ 'timeout' => 1800, // 2分钟超时
|
|
|
+ 'connect_timeout' => 10, // 连接超时10秒
|
|
|
+ 'http_errors' => false, // 不自动抛出HTTP错误
|
|
|
+ 'verify' => false, // 禁用SSL验证(如果有证书问题)
|
|
|
+ 'allow_redirects' => true,
|
|
|
]);
|
|
|
|
|
|
// 根据是否有参考图选择不同的请求方式
|
|
|
if ($isEditMode) {
|
|
|
- // 使用 multipart/form-data 格式
|
|
|
+ // 使用原生 cURL 处理 multipart/form-data 格式
|
|
|
$imageUrl = is_array($task->ref_img_url) ? $task->ref_img_url[0] : $task->ref_img_url;
|
|
|
|
|
|
dLog('generate')->info('下载参考图片', ['image_url' => $imageUrl]);
|
|
|
|
|
|
- // 下载图片到临时文件
|
|
|
+ // 下载图片内容
|
|
|
$imageContent = @file_get_contents($imageUrl);
|
|
|
if ($imageContent === false) {
|
|
|
throw new \Exception('下载参考图片失败: ' . $imageUrl);
|
|
|
}
|
|
|
|
|
|
- $tempFile = tempnam(sys_get_temp_dir(), 'gpt_image_');
|
|
|
+ dLog('generate')->info('参考图片下载完成', ['size' => strlen($imageContent)]);
|
|
|
+
|
|
|
+ // 保存为临时文件(cURL 需要真实文件路径)
|
|
|
+ $extension = pathinfo(parse_url($imageUrl, PHP_URL_PATH), PATHINFO_EXTENSION) ?: 'png';
|
|
|
+ $tempFile = tempnam(sys_get_temp_dir(), 'gpt_img_') . '.' . $extension;
|
|
|
file_put_contents($tempFile, $imageContent);
|
|
|
|
|
|
- dLog('generate')->info('参考图片已保存到临时文件', ['temp_file' => $tempFile, 'size' => filesize($tempFile)]);
|
|
|
+ dLog('generate')->info('临时文件创建成功', ['file' => $tempFile, 'size' => filesize($tempFile)]);
|
|
|
|
|
|
- $multipart = [
|
|
|
- [
|
|
|
- 'name' => 'image',
|
|
|
- 'contents' => fopen($tempFile, 'r'),
|
|
|
- 'filename' => basename($imageUrl),
|
|
|
- ],
|
|
|
- [
|
|
|
- 'name' => 'prompt',
|
|
|
- 'contents' => $task->prompt,
|
|
|
- ],
|
|
|
- [
|
|
|
- 'name' => 'model',
|
|
|
- 'contents' => 'gpt-image-2',
|
|
|
- ],
|
|
|
+ // 构建 POST 字段
|
|
|
+ $postFields = [
|
|
|
+ 'image' => new \CURLFile($tempFile, 'image/' . $extension, 'image.' . $extension),
|
|
|
+ 'prompt' => $task->prompt,
|
|
|
+ 'model' => 'gpt-image-2',
|
|
|
+ 'n' => (int)$task->image_num,
|
|
|
];
|
|
|
|
|
|
// 尺寸参数
|
|
|
if ($task->width && $task->height) {
|
|
|
- $multipart[] = [
|
|
|
- 'name' => 'size',
|
|
|
- 'contents' => $task->width . 'x' . $task->height,
|
|
|
- ];
|
|
|
+ $postFields['size'] = $task->width . 'x' . $task->height;
|
|
|
}
|
|
|
|
|
|
- dLog('generate')->info('GPT-Image2 API参数(编辑模式)', ['api_url' => $api_url, 'prompt' => $task->prompt, 'image_url' => $imageUrl, 'size' => ($task->width . 'x' . $task->height)]);
|
|
|
+ dLog('generate')->info('GPT-Image2 API参数(编辑模式-cURL)', ['api_url' => $api_url, 'prompt' => $task->prompt, 'size' => ($task->width . 'x' . $task->height), 'n' => (int)$task->image_num]);
|
|
|
logDB('generate', 'info', 'GPT-Image2任务提交(编辑模式)', ['task_id' => $task->task_id, 'api_url' => $api_url, 'image_url' => $imageUrl, 'prompt' => $task->prompt]);
|
|
|
|
|
|
- dLog('generate')->info('开始发送multipart请求到GPT-Image2 API');
|
|
|
$startTime = microtime(true);
|
|
|
|
|
|
- // 调用GPT-Image2 API (multipart/form-data)
|
|
|
- $response = $gptClient->post($api_url, [
|
|
|
- 'headers' => [
|
|
|
- 'Authorization' => 'Bearer ' . $apiKey,
|
|
|
- 'Accept' => 'application/json',
|
|
|
+ // 使用原生 cURL
|
|
|
+ $ch = curl_init();
|
|
|
+ curl_setopt_array($ch, [
|
|
|
+ CURLOPT_URL => $api_url,
|
|
|
+ CURLOPT_RETURNTRANSFER => true,
|
|
|
+ CURLOPT_POST => true,
|
|
|
+ CURLOPT_POSTFIELDS => $postFields,
|
|
|
+ CURLOPT_HTTPHEADER => [
|
|
|
+ 'Authorization: Bearer ' . $apiKey,
|
|
|
+ 'Accept: application/json',
|
|
|
+ 'Expect:', // 移除 Expect: 100-continue,避免等待
|
|
|
],
|
|
|
- 'multipart' => $multipart,
|
|
|
+ CURLOPT_TIMEOUT => 1800, // 不设超时,等待完成
|
|
|
+ CURLOPT_CONNECTTIMEOUT => 30,
|
|
|
+ CURLOPT_SSL_VERIFYPEER => false,
|
|
|
+ CURLOPT_SSL_VERIFYHOST => false,
|
|
|
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, // 强制使用 HTTP/1.1
|
|
|
]);
|
|
|
|
|
|
- $elapsed = round(microtime(true) - $startTime, 2);
|
|
|
- dLog('generate')->info('GPT-Image2 API请求完成', ['elapsed' => $elapsed . 's', 'status_code' => $response->getStatusCode()]);
|
|
|
+ dLog('generate')->info('开始发送cURL请求');
|
|
|
+
|
|
|
+ $responseBody = curl_exec($ch);
|
|
|
+ $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
+ $curlError = curl_error($ch);
|
|
|
+ $curlErrno = curl_errno($ch);
|
|
|
+ curl_close($ch);
|
|
|
|
|
|
// 删除临时文件
|
|
|
@unlink($tempFile);
|
|
|
+
|
|
|
+ $elapsed = round(microtime(true) - $startTime, 2);
|
|
|
+ dLog('generate')->info('cURL请求完成', ['elapsed' => $elapsed . 's', 'http_code' => $statusCode, 'curl_errno' => $curlErrno]);
|
|
|
+
|
|
|
+ if ($curlErrno !== 0) {
|
|
|
+ throw new \Exception('cURL错误: ' . $curlError . ' (错误码: ' . $curlErrno . ')');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (empty($responseBody)) {
|
|
|
+ throw new \Exception('API返回空响应,HTTP状态码: ' . $statusCode);
|
|
|
+ }
|
|
|
} else {
|
|
|
// 使用 JSON 格式 (原有的文生图模式)
|
|
|
$apiParams = [
|
|
|
@@ -339,7 +358,6 @@ class AIImageGenerationService
|
|
|
dLog('generate')->info('GPT-Image2 API参数', $apiParams);
|
|
|
logDB('generate', 'info', 'GPT-Image2任务提交', ['task_id' => $task->task_id, 'api_url' => $api_url, 'params' => $apiParams]);
|
|
|
|
|
|
- dLog('generate')->info('开始发送JSON请求到GPT-Image2 API');
|
|
|
$startTime = microtime(true);
|
|
|
|
|
|
// 调用GPT-Image2 API
|
|
|
@@ -351,21 +369,19 @@ class AIImageGenerationService
|
|
|
'json' => $apiParams,
|
|
|
]);
|
|
|
|
|
|
+ $statusCode = $response->getStatusCode();
|
|
|
+ $responseBody = $response->getBody()->getContents();
|
|
|
+
|
|
|
$elapsed = round(microtime(true) - $startTime, 2);
|
|
|
- dLog('generate')->info('GPT-Image2 API请求完成', ['elapsed' => $elapsed . 's', 'status_code' => $response->getStatusCode()]);
|
|
|
+ dLog('generate')->info('Guzzle请求完成', ['elapsed' => $elapsed . 's', 'http_code' => $statusCode]);
|
|
|
+
|
|
|
+ if (empty($responseBody)) {
|
|
|
+ throw new \Exception('API返回空响应,HTTP状态码: ' . $statusCode);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 获取响应状态码
|
|
|
- $statusCode = $response->getStatusCode();
|
|
|
- dLog('generate')->info('GPT-Image2 API HTTP状态码: ' . $statusCode);
|
|
|
-
|
|
|
- // 读取响应内容
|
|
|
- $responseBody = $response->getBody()->getContents();
|
|
|
- dLog('generate')->info('GPT-Image2 API响应原始数据长度: ' . strlen($responseBody));
|
|
|
-
|
|
|
- if (empty($responseBody)) {
|
|
|
- throw new \Exception('API返回空响应,HTTP状态码: ' . $statusCode);
|
|
|
- }
|
|
|
+ // 解析响应
|
|
|
+ dLog('generate')->info('响应数据长度', ['length' => strlen($responseBody)]);
|
|
|
|
|
|
$responseData = json_decode($responseBody, true);
|
|
|
|