fix: scene fusion retry now correctly handles streaming response and batch download works for all images
This commit is contained in:
parent
ee4bb14736
commit
0d69e8d9fa
@ -253,25 +253,76 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
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') {
|
||||
setScenes(prev => prev.map(s =>
|
||||
s.id === id ? {
|
||||
...s,
|
||||
status: 'completed',
|
||||
result: result.result,
|
||||
originalUrl: result.originalUrl,
|
||||
} : s
|
||||
));
|
||||
toast.success('重新生成成功');
|
||||
} else {
|
||||
throw new Error(result?.error || '生成失败');
|
||||
// 处理流式响应
|
||||
const reader = response.body?.getReader();
|
||||
if (!reader) {
|
||||
throw new Error('无法读取响应流');
|
||||
}
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
let success = false;
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
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) {
|
||||
console.error('[SceneFusion] Regenerate error:', error);
|
||||
@ -282,17 +333,30 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps
|
||||
}
|
||||
};
|
||||
|
||||
// 下载图片
|
||||
const downloadImage = (url: string, filename: string) => {
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
// 下载图片 - 使用 fetch 获取图片数据后下载,解决跨域问题
|
||||
const downloadImage = async (url: string, filename: string) => {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const blob = await response.blob();
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
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 completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result));
|
||||
if (completedScenes.length === 0) {
|
||||
@ -300,16 +364,22 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps
|
||||
return;
|
||||
}
|
||||
|
||||
toast.info(`正在下载 ${completedScenes.length} 张图片...`);
|
||||
toast.info(`正在下载 ${completedScenes.length} 张图片,请稍候...`);
|
||||
|
||||
// 顺序下载,每张图片下载完成后再下载下一张
|
||||
for (let i = 0; i < completedScenes.length; i++) {
|
||||
const scene = completedScenes[i];
|
||||
const url = scene.originalUrl || scene.result!;
|
||||
// 延迟下载避免浏览器阻止
|
||||
setTimeout(() => {
|
||||
downloadImage(url, `scene-fusion-${i + 1}.png`);
|
||||
}, i * 300);
|
||||
try {
|
||||
await downloadImage(url, `scene-fusion-${i + 1}.png`);
|
||||
// 添加小延迟避免浏览器阻止
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
} catch (error) {
|
||||
console.error(`下载第 ${i + 1} 张图片失败:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
toast.success(`已下载 ${completedScenes.length} 张图片`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -337,8 +337,8 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
|
||||
body: JSON.stringify({
|
||||
productImage,
|
||||
productName: productName || undefined,
|
||||
aspectRatio, // 图片比例
|
||||
imageSize, // 图片分辨率
|
||||
aspectRatio,
|
||||
imageSize,
|
||||
scenes: [{
|
||||
id: scene.id,
|
||||
image: scene.image,
|
||||
@ -348,25 +348,76 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
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') {
|
||||
setScenes(prev => prev.map(s =>
|
||||
s.id === id ? {
|
||||
...s,
|
||||
status: 'completed',
|
||||
result: result.result,
|
||||
originalUrl: result.originalUrl,
|
||||
} : s
|
||||
));
|
||||
toast.success('重新生成成功');
|
||||
} else {
|
||||
throw new Error(result?.error || '生成失败');
|
||||
// 处理流式响应
|
||||
const reader = response.body?.getReader();
|
||||
if (!reader) {
|
||||
throw new Error('无法读取响应流');
|
||||
}
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
let success = false;
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
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) {
|
||||
console.error('[SceneFusion] Regenerate error:', error);
|
||||
@ -377,17 +428,30 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
|
||||
}
|
||||
};
|
||||
|
||||
// 下载图片
|
||||
const downloadImage = (url: string, filename: string) => {
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
// 下载图片 - 使用 fetch 获取图片数据后下载,解决跨域问题
|
||||
const downloadImage = async (url: string, filename: string) => {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const blob = await response.blob();
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
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 completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result));
|
||||
if (completedScenes.length === 0) {
|
||||
@ -395,16 +459,22 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
toast.info(`正在下载 ${completedScenes.length} 张图片...`);
|
||||
toast.info(`正在下载 ${completedScenes.length} 张图片,请稍候...`);
|
||||
|
||||
// 顺序下载,每张图片下载完成后再下载下一张
|
||||
for (let i = 0; i < completedScenes.length; i++) {
|
||||
const scene = completedScenes[i];
|
||||
const url = scene.originalUrl || scene.result!;
|
||||
// 延迟下载避免浏览器阻止
|
||||
setTimeout(() => {
|
||||
downloadImage(url, `scene-fusion-${i + 1}.png`);
|
||||
}, i * 300);
|
||||
try {
|
||||
await downloadImage(url, `scene-fusion-${i + 1}.png`);
|
||||
// 添加小延迟避免浏览器阻止
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
} catch (error) {
|
||||
console.error(`下载第 ${i + 1} 张图片失败:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
toast.success(`已下载 ${completedScenes.length} 张图片`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user