refactor(media): 资源分发 - 统一资源访问策略与云存储重定向

This commit is contained in:
ddCat
2026-09-22 21:41:00 +08:00
parent af6633047c
commit 487c34bfeb
50 changed files with 2112 additions and 1266 deletions

View File

@@ -11,7 +11,7 @@ import { useConfigStore, useEffectiveConfig } from "@/stores/use-config-store";
import { uploadMediaFile } from "@/services/file-storage";
import { createCanvasGenerationLiveProjectAdapter, registerCanvasGenerationLiveProject } from "@/services/canvas-generation-consumer";
import { getActiveUserScope } from "@/lib/user-scope";
import { resourceFileUrl, resourceIdFromStorageKey, syncResourceToArkPrivateAsset } from "@/services/api/resources";
import { getResourceAccess, resolveResourceAccessURL, resourceFileUrl, resourceIdFromStorageKey, syncResourceToArkPrivateAsset } from "@/services/api/resources";
import { uploadImage } from "@/services/image-storage";
import { imageMetadata } from "@/lib/canvas/canvas-generation-task-sync";
import { isCanvasImageSourceNode } from "@/lib/canvas/canvas-image-source";
@@ -188,6 +188,11 @@ async function copyImageToSystemClipboard(source: string, storageKey?: string) {
let sourceBlob: Blob | null = null;
if (storageKey) {
sourceBlob = await getCachedResourceBlob(storageKey).catch(() => null);
// A resource-backed image must be read through the resource access
// contract. Do not fall back to fetch(source) here: local/proxy
// deliveries may require the session cookie and a separate API
// origin, while CDN deliveries must omit that cookie.
if (!sourceBlob) throw new Error("图片资源读取失败");
}
if (!sourceBlob) {
const response = await fetch(source);
@@ -1742,16 +1747,22 @@ function InfiniteCanvasPage() {
if (copyingNodeContentRef.current) return;
copyingNodeContentRef.current = true;
releaseCopiedNodesPastePriority();
const content = node?.metadata?.content?.trim();
const resourceId = resourceIdFromStorageKey(node?.metadata?.storageKey);
const copySource = content || (node?.type === CanvasNodeType.Image && resourceId ? resourceFileUrl(resourceId) : "");
if (!node || !copySource) {
if (!node) {
copyingNodeContentRef.current = false;
message.warning("没有可复制的内容");
return;
}
try {
const content = node.metadata?.content?.trim();
const resourceId = resourceIdFromStorageKey(node.metadata?.storageKey);
// Resource-backed media must use the central access contract. This keeps
// copy operations on the configured CDN/OSS URL instead of copying the
// platform file endpoint or a stale URL persisted in canvas metadata.
const copySource = resourceId
? resolveResourceAccessURL((await getResourceAccess(`resource:${resourceId}`, "copy")).url)
: content || "";
if (!copySource) throw new Error("没有可复制的内容");
if (node.type === CanvasNodeType.Image) {
try {
await copyImageToSystemClipboard(copySource, node.metadata?.storageKey);
@@ -1791,8 +1802,10 @@ function InfiniteCanvasPage() {
const storageKey = node?.metadata?.storageKey;
const content = node?.metadata?.content?.trim();
const resourceId = resourceIdFromStorageKey(storageKey);
const mediaPath = content && !content.startsWith("data:") && !content.startsWith("blob:") ? content : resourceId ? resourceFileUrl(resourceId) : "";
const mediaURL = mediaPath ? new URL(mediaPath, window.location.href).toString() : "";
const mediaPath = content && !content.startsWith("data:") && !content.startsWith("blob:") ? content : "";
const mediaURL = resourceId
? resolveResourceAccessURL((await getResourceAccess(`resource:${resourceId}`, "copy")).url)
: mediaPath ? new URL(mediaPath, window.location.href).toString() : "";
if (!mediaURL) throw new Error("当前媒体只有本地内容,没有可复制的地址");
if (navigator.clipboard?.writeText) await navigator.clipboard.writeText(mediaURL);
else if (!(await copyToClipboard(mediaURL))) throw new Error("当前浏览器不支持写入剪贴板");