fix: scene fusion retry now correctly handles streaming response and batch download works for all images

This commit is contained in:
Your Name 2026-01-27 21:40:46 +08:00
parent ee4bb14736
commit 0d69e8d9fa
2 changed files with 202 additions and 62 deletions

View File

@ -253,25 +253,76 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps
}), }),
}); });
const data = await response.json();
if (!response.ok) { if (!response.ok) {
throw new Error(data.error || '处理失败'); // 尝试解析错误信息
try {
const errorData = await response.json();
throw new Error(errorData.error || '处理失败');
} catch {
throw new Error(`请求失败: ${response.status}`);
}
} }
const result = data.results?.[0]; // 处理流式响应
if (result?.status === 'completed') { const reader = response.body?.getReader();
setScenes(prev => prev.map(s => if (!reader) {
s.id === id ? { throw new Error('无法读取响应流');
...s, }
status: 'completed',
result: result.result, const decoder = new TextDecoder();
originalUrl: result.originalUrl, let buffer = '';
} : s let success = false;
));
toast.success('重新生成成功'); while (true) {
} else { const { done, value } = await reader.read();
throw new Error(result?.error || '生成失败');
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.trim()) continue;
try {
const message = JSON.parse(line);
console.log('[SceneFusion] Regenerate message:', message.type);
if (message.type === 'result') {
const result = message.data;
if (result.id === id) {
if (result.status === 'completed') {
setScenes(prev => prev.map(s =>
s.id === id ? {
...s,
status: 'completed',
result: result.result,
originalUrl: result.originalUrl,
} : s
));
toast.success('重新生成成功');
success = true;
} else {
throw new Error(result.error || '生成失败');
}
}
} else if (message.type === 'error') {
throw new Error(message.error || '处理失败');
}
} catch (parseError) {
if (parseError instanceof SyntaxError) {
console.error('[SceneFusion] Failed to parse message:', line);
} else {
throw parseError;
}
}
}
}
if (!success) {
throw new Error('未收到有效的生成结果');
} }
} catch (error) { } catch (error) {
console.error('[SceneFusion] Regenerate error:', error); console.error('[SceneFusion] Regenerate error:', error);
@ -282,17 +333,30 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps
} }
}; };
// 下载图片 // 下载图片 - 使用 fetch 获取图片数据后下载,解决跨域问题
const downloadImage = (url: string, filename: string) => { const downloadImage = async (url: string, filename: string) => {
const link = document.createElement('a'); try {
link.href = url; const response = await fetch(url);
link.download = filename; const blob = await response.blob();
document.body.appendChild(link); const blobUrl = URL.createObjectURL(blob);
link.click();
document.body.removeChild(link); const link = document.createElement('a');
link.href = blobUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 释放 Blob URL
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
} catch (error) {
console.error('下载图片失败:', error);
// 降级方案:直接打开链接
window.open(url, '_blank');
}
}; };
// 全部下载 // 全部下载 - 顺序下载所有图片
const downloadAll = async () => { const downloadAll = async () => {
const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result)); const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result));
if (completedScenes.length === 0) { if (completedScenes.length === 0) {
@ -300,16 +364,22 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps
return; return;
} }
toast.info(`正在下载 ${completedScenes.length} 张图片...`); toast.info(`正在下载 ${completedScenes.length} 张图片,请稍候...`);
// 顺序下载,每张图片下载完成后再下载下一张
for (let i = 0; i < completedScenes.length; i++) { for (let i = 0; i < completedScenes.length; i++) {
const scene = completedScenes[i]; const scene = completedScenes[i];
const url = scene.originalUrl || scene.result!; const url = scene.originalUrl || scene.result!;
// 延迟下载避免浏览器阻止 try {
setTimeout(() => { await downloadImage(url, `scene-fusion-${i + 1}.png`);
downloadImage(url, `scene-fusion-${i + 1}.png`); // 添加小延迟避免浏览器阻止
}, i * 300); await new Promise(resolve => setTimeout(resolve, 500));
} catch (error) {
console.error(`下载第 ${i + 1} 张图片失败:`, error);
}
} }
toast.success(`已下载 ${completedScenes.length} 张图片`);
}; };
return ( return (

View File

@ -337,8 +337,8 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
body: JSON.stringify({ body: JSON.stringify({
productImage, productImage,
productName: productName || undefined, productName: productName || undefined,
aspectRatio, // 图片比例 aspectRatio,
imageSize, // 图片分辨率 imageSize,
scenes: [{ scenes: [{
id: scene.id, id: scene.id,
image: scene.image, image: scene.image,
@ -348,25 +348,76 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
}), }),
}); });
const data = await response.json();
if (!response.ok) { if (!response.ok) {
throw new Error(data.error || '处理失败'); // 尝试解析错误信息
try {
const errorData = await response.json();
throw new Error(errorData.error || '处理失败');
} catch {
throw new Error(`请求失败: ${response.status}`);
}
} }
const result = data.results?.[0]; // 处理流式响应
if (result?.status === 'completed') { const reader = response.body?.getReader();
setScenes(prev => prev.map(s => if (!reader) {
s.id === id ? { throw new Error('无法读取响应流');
...s, }
status: 'completed',
result: result.result, const decoder = new TextDecoder();
originalUrl: result.originalUrl, let buffer = '';
} : s let success = false;
));
toast.success('重新生成成功'); while (true) {
} else { const { done, value } = await reader.read();
throw new Error(result?.error || '生成失败');
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.trim()) continue;
try {
const message = JSON.parse(line);
console.log('[SceneFusion] Regenerate message:', message.type);
if (message.type === 'result') {
const result = message.data;
if (result.id === id) {
if (result.status === 'completed') {
setScenes(prev => prev.map(s =>
s.id === id ? {
...s,
status: 'completed',
result: result.result,
originalUrl: result.originalUrl,
} : s
));
toast.success('重新生成成功');
success = true;
} else {
throw new Error(result.error || '生成失败');
}
}
} else if (message.type === 'error') {
throw new Error(message.error || '处理失败');
}
} catch (parseError) {
if (parseError instanceof SyntaxError) {
console.error('[SceneFusion] Failed to parse message:', line);
} else {
throw parseError;
}
}
}
}
if (!success) {
throw new Error('未收到有效的生成结果');
} }
} catch (error) { } catch (error) {
console.error('[SceneFusion] Regenerate error:', error); console.error('[SceneFusion] Regenerate error:', error);
@ -377,17 +428,30 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
} }
}; };
// 下载图片 // 下载图片 - 使用 fetch 获取图片数据后下载,解决跨域问题
const downloadImage = (url: string, filename: string) => { const downloadImage = async (url: string, filename: string) => {
const link = document.createElement('a'); try {
link.href = url; const response = await fetch(url);
link.download = filename; const blob = await response.blob();
document.body.appendChild(link); const blobUrl = URL.createObjectURL(blob);
link.click();
document.body.removeChild(link); const link = document.createElement('a');
link.href = blobUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 释放 Blob URL
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
} catch (error) {
console.error('下载图片失败:', error);
// 降级方案:直接打开链接
window.open(url, '_blank');
}
}; };
// 全部下载 // 全部下载 - 顺序下载所有图片
const downloadAll = async () => { const downloadAll = async () => {
const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result)); const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result));
if (completedScenes.length === 0) { if (completedScenes.length === 0) {
@ -395,16 +459,22 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
return; return;
} }
toast.info(`正在下载 ${completedScenes.length} 张图片...`); toast.info(`正在下载 ${completedScenes.length} 张图片,请稍候...`);
// 顺序下载,每张图片下载完成后再下载下一张
for (let i = 0; i < completedScenes.length; i++) { for (let i = 0; i < completedScenes.length; i++) {
const scene = completedScenes[i]; const scene = completedScenes[i];
const url = scene.originalUrl || scene.result!; const url = scene.originalUrl || scene.result!;
// 延迟下载避免浏览器阻止 try {
setTimeout(() => { await downloadImage(url, `scene-fusion-${i + 1}.png`);
downloadImage(url, `scene-fusion-${i + 1}.png`); // 添加小延迟避免浏览器阻止
}, i * 300); await new Promise(resolve => setTimeout(resolve, 500));
} catch (error) {
console.error(`下载第 ${i + 1} 张图片失败:`, error);
}
} }
toast.success(`已下载 ${completedScenes.length} 张图片`);
}; };
return ( return (