fix: harden image download fallbacks
This commit is contained in:
parent
2f52589469
commit
2babdd6a47
@ -481,6 +481,25 @@ function buildDownloadFilename(url: string): string {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function triggerBrowserDownload(
|
||||
url: string,
|
||||
filename?: string,
|
||||
options?: { openInNewTab?: boolean }
|
||||
) {
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
if (filename) {
|
||||
link.download = filename;
|
||||
}
|
||||
if (options?.openInNewTab) {
|
||||
link.target = "_blank";
|
||||
}
|
||||
link.rel = "noopener noreferrer";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
function getAssistantImageNodeIdentityKey(node: any): string {
|
||||
if (!node || node.type !== "image") return "";
|
||||
const data = ((node.data as any) || {}) as Record<string, any>;
|
||||
@ -2482,22 +2501,20 @@ export function CanvasChatPanel({
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const blobUrl = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = blobUrl;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
triggerBrowserDownload(blobUrl, filename);
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(blobUrl), 1000);
|
||||
return;
|
||||
} catch {
|
||||
const link = document.createElement("a");
|
||||
link.href = originalUrl;
|
||||
link.target = "_blank";
|
||||
link.rel = "noopener noreferrer";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
} catch (error) {
|
||||
console.error("下载原图失败:", error);
|
||||
try {
|
||||
triggerBrowserDownload(originalUrl, filename);
|
||||
toast.message("代理下载失败,已切换为原图直链下载");
|
||||
return;
|
||||
} catch (fallbackError) {
|
||||
console.error("原图直链下载失败:", fallbackError);
|
||||
}
|
||||
|
||||
triggerBrowserDownload(originalUrl, undefined, { openInNewTab: true });
|
||||
toast.message("已打开原图,可右键另存为");
|
||||
}
|
||||
}, []);
|
||||
|
||||
@ -48,6 +48,7 @@ import {
|
||||
normalizeCanvasToolbarConfig,
|
||||
type CanvasToolbarConfig,
|
||||
} from "@/lib/canvas-toolbar-config";
|
||||
import { inferOriginalImageUrl } from "@/lib/image-url";
|
||||
|
||||
const VIDEO_CANVAS_SYMBOL_ICON_MAP: Record<string, LucideIcon> = {
|
||||
idea: Lightbulb,
|
||||
@ -1170,7 +1171,7 @@ export function CanvasWorkspace({
|
||||
const nodeData = node.data as any;
|
||||
const rawImageUrl = String(nodeData?.src || "").trim();
|
||||
if (!rawImageUrl) return null;
|
||||
let imageUrl = rawImageUrl;
|
||||
let imageUrl = inferOriginalImageUrl(rawImageUrl) || rawImageUrl;
|
||||
if (browserOrigin && rawImageUrl.startsWith("/")) {
|
||||
imageUrl = `${browserOrigin}${rawImageUrl}`;
|
||||
} else if (browserOrigin && rawImageUrl.startsWith("//")) {
|
||||
|
||||
@ -1158,6 +1158,25 @@ const sanitizeFileName = (name: string) => {
|
||||
return safe || "image";
|
||||
};
|
||||
|
||||
const triggerBrowserDownload = (
|
||||
url: string,
|
||||
filename?: string,
|
||||
options?: { openInNewTab?: boolean }
|
||||
) => {
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
if (filename) {
|
||||
link.download = filename;
|
||||
}
|
||||
if (options?.openInNewTab) {
|
||||
link.target = "_blank";
|
||||
}
|
||||
link.rel = "noopener noreferrer";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
const GENERIC_CANVAS_PROMPTS = new Set([
|
||||
"对话生成",
|
||||
"图片生成",
|
||||
@ -6083,7 +6102,15 @@ export function ImageNode({
|
||||
// 下载图片 - 通过 fetch 获取图片数据实现真正的下载(避免跨域问题导致跳转)
|
||||
const handleDownload = async () => {
|
||||
if (!data.src) return;
|
||||
const originalUrl = inferOriginalImageUrl(data.src) || data.src;
|
||||
const originalUrl = String(inferOriginalImageUrl(data.src) || data.src || "").trim();
|
||||
if (!originalUrl) {
|
||||
toast.error("下载失败,请重试");
|
||||
return;
|
||||
}
|
||||
|
||||
const urlParts = originalUrl.split(".");
|
||||
const ext = urlParts[urlParts.length - 1]?.split("?")[0] || "png";
|
||||
const filename = `${sanitizeFileName(displayTitle)}.${ext}`;
|
||||
|
||||
try {
|
||||
toast.info("正在准备下载...");
|
||||
@ -6102,11 +6129,7 @@ export function ImageNode({
|
||||
// 创建下载链接
|
||||
const link = document.createElement("a");
|
||||
link.href = blobUrl;
|
||||
|
||||
// 从 URL 中提取文件扩展名,默认使用 png
|
||||
const urlParts = originalUrl.split(".");
|
||||
const ext = urlParts[urlParts.length - 1]?.split("?")[0] || "png";
|
||||
link.download = `${sanitizeFileName(displayTitle)}.${ext}`;
|
||||
link.download = filename;
|
||||
|
||||
// 触发下载
|
||||
document.body.appendChild(link);
|
||||
@ -6119,10 +6142,21 @@ export function ImageNode({
|
||||
toast.success("下载成功");
|
||||
} catch (error) {
|
||||
console.error("下载失败:", error);
|
||||
toast.error("下载失败,请重试");
|
||||
try {
|
||||
triggerBrowserDownload(originalUrl, filename);
|
||||
toast.message("代理下载失败,已切换为原图直链下载");
|
||||
return;
|
||||
} catch (fallbackError) {
|
||||
console.error("原图直链下载失败:", fallbackError);
|
||||
}
|
||||
|
||||
// 降级方案:直接打开图片
|
||||
window.open(originalUrl, "_blank", "noopener,noreferrer");
|
||||
try {
|
||||
triggerBrowserDownload(originalUrl, undefined, { openInNewTab: true });
|
||||
toast.message("已打开原图,可直接另存为");
|
||||
} catch (openError) {
|
||||
console.error("打开原图失败:", openError);
|
||||
toast.error("下载失败,请重试");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -7,6 +7,25 @@ import { ImageAnnotator } from "./image-annotator";
|
||||
import { AngleEditDialog } from "./angle-edit-dialog";
|
||||
import { buildImageProxyUrl } from "@/lib/image-delivery";
|
||||
|
||||
function triggerBrowserDownload(
|
||||
url: string,
|
||||
filename?: string,
|
||||
options?: { openInNewTab?: boolean }
|
||||
) {
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
if (filename) {
|
||||
link.download = filename;
|
||||
}
|
||||
if (options?.openInNewTab) {
|
||||
link.target = "_blank";
|
||||
}
|
||||
link.rel = "noopener noreferrer";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
export function ImagePreview({
|
||||
src,
|
||||
alt,
|
||||
@ -184,19 +203,39 @@ export function ImagePreview({
|
||||
|
||||
// 下载图片函数(下载原图)
|
||||
const handleDownload = async () => {
|
||||
const safeOriginalUrl = String(original || "").trim();
|
||||
if (!safeOriginalUrl) {
|
||||
toast.error("下载失败,请重试");
|
||||
return;
|
||||
}
|
||||
const filename = `generated-image-${Date.now()}.jpg`;
|
||||
try {
|
||||
const response = await fetch(original);
|
||||
const response = await fetch(
|
||||
buildImageProxyUrl(safeOriginalUrl, { delivery: "proxy" })
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`下载失败(${response.status})`);
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `gemini-image-${Date.now()}.jpg`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
triggerBrowserDownload(url, filename);
|
||||
window.URL.revokeObjectURL(url);
|
||||
toast.success("下载成功");
|
||||
} catch (error) {
|
||||
console.error("下载失败:", error);
|
||||
try {
|
||||
triggerBrowserDownload(safeOriginalUrl, filename);
|
||||
toast.message("代理下载失败,已切换为原图直链下载");
|
||||
} catch (fallbackError) {
|
||||
console.error("原图直链下载失败:", fallbackError);
|
||||
try {
|
||||
triggerBrowserDownload(safeOriginalUrl, undefined, { openInNewTab: true });
|
||||
toast.message("已打开原图,可直接另存为");
|
||||
} catch (openError) {
|
||||
console.error("打开原图失败:", openError);
|
||||
toast.error("下载失败,请重试");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user