修复生成确认与素材导出

补齐外部生成确认请求的 JSON Content-Type

本地屏蔽已确认终态任务避免轮询重复弹窗

单素材导出改为读取 Blob 后触发浏览器下载

序列帧导出按 frame objectKey 换签读取
This commit is contained in:
2026-06-24 20:35:03 +08:00
parent b6c5fdeb30
commit db056423de
7 changed files with 250 additions and 50 deletions
@@ -8,6 +8,7 @@ import {
getLayerAssetExtensionFromTypeOrSrc,
getLayerExportKey,
readLayerAssetBlob,
readLayerImageSequenceFrameBlob,
sanitizeExportFilePart,
} from './ImageCanvasExportModel';
import type { CanvasLayer } from './ImageCanvasEditorTypes';
@@ -215,6 +216,70 @@ describe('ImageCanvasExportModel', () => {
globalThis.fetch = originalFetch;
}
});
it('reads private image-sequence frame object keys through signed URLs', async () => {
const originalFetch = globalThis.fetch;
const fetchMock = vi.fn(async (url: string) => {
if (url.startsWith('/api/assets/read-url?')) {
return new Response(
JSON.stringify({
ok: true,
data: {
read: {
objectKey: 'generated/frame-1.png',
signedUrl:
'https://oss.example.com/generated/frame-1.png?x-oss-signature=1',
expiresAt: '2026-06-20T00:00:00.000Z',
},
},
error: null,
meta: { apiVersion: '2026-06-16' },
}),
);
}
if (
url === 'https://oss.example.com/generated/frame-1.png?x-oss-signature=1'
) {
return new Response(new Blob(['frame'], { type: 'image/png' }));
}
return new Response(null, { status: 404 });
});
globalThis.fetch = fetchMock as typeof fetch;
try {
const blob = await readLayerImageSequenceFrameBlob(
buildLayer({
mediaType: 'image-sequence',
imageSequenceFrames: [
{
frameIndex: 1,
imageSrc: '/generated-editor-frames/frame-1.png',
objectKey: 'generated/frame-1.png',
width: 512,
height: 512,
},
],
}),
{
frameIndex: 1,
imageSrc: '/generated-editor-frames/frame-1.png',
objectKey: 'generated/frame-1.png',
width: 512,
height: 512,
},
);
expect(blob).toBeTruthy();
expect(fetchMock).toHaveBeenCalledWith(
expect.stringContaining(
'/api/assets/read-url?objectKey=generated%2Fframe-1.png',
),
expect.any(Object),
);
} finally {
globalThis.fetch = originalFetch;
}
});
});
function buildLayer(overrides: Partial<CanvasLayer> = {}): CanvasLayer {