/** Downloads through the authenticated canvas export route, including cross-origin media. */ export async function downloadCanvasMedia(files: Array<{ url: string; name: string }>) { if (!files.length) throw Error("没有可下载的素材"); if (files.length > 200) throw Error("每次最多下载 200 项,请分批选择"); const batch = files.length > 1; const response = await fetch(batch ? "/api/canvas/download-zip" : "/api/canvas/download-image", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(batch ? { files, folderName: "画布素材" } : files[0]), signal: AbortSignal.timeout(300000), }); if (!response.ok) { const data = await response.json().catch(() => ({})); throw Error(data.error || "素材下载失败"); } const blob = await response.blob(); if (!blob.size) throw Error("返回的下载文件为空"); const encodedName = response.headers .get("content-disposition") ?.match(/filename\*=UTF-8''([^;]+)/i)?.[1]; const filename = encodedName ? decodeURIComponent(encodedName) : batch ? "画布素材.zip" : files[0].name; const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); link.remove(); window.setTimeout(() => URL.revokeObjectURL(url), 60000); return { failed: Number(response.headers.get("X-Download-Failed-Count") || 0) }; }