mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 08:54:23 +08:00
de25ef515e
- 引入 file-saver 库替代手动创建下载链接的方式 - 在 asset-transfer.ts 中使用 saveAs 替代 URL.createObjectURL 和手动点击链接 - 在 canvas-export.ts 中使用 saveAs 替代原有的文件下载逻辑 - 更新 package.json 添加 file-saver 依赖 - 更新 bun.lock 文件同步依赖变更
73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
import { saveAs } from "file-saver";
|
|
|
|
import { createZip, readZip } from "@/lib/zip";
|
|
import { getMediaBlob, setMediaBlob } from "@/services/file-storage";
|
|
import { getImageBlob, setImageBlob } from "@/services/image-storage";
|
|
import type { Asset } from "@/stores/use-asset-store";
|
|
|
|
type AssetExportFile = {
|
|
app: "infinite-canvas";
|
|
version: 1;
|
|
exportedAt: string;
|
|
assets: Asset[];
|
|
files: AssetExportItem[];
|
|
};
|
|
|
|
type AssetExportItem = {
|
|
storageKey: string;
|
|
path: string;
|
|
mimeType: string;
|
|
bytes: number;
|
|
};
|
|
|
|
export async function exportAssets(assets: Asset[]) {
|
|
const files: AssetExportItem[] = [];
|
|
const zipFiles: { name: string; data: BlobPart }[] = [];
|
|
|
|
await Promise.all(
|
|
assets.map(async (asset) => {
|
|
const storageKey = asset.kind === "image" || asset.kind === "video" ? asset.data.storageKey : undefined;
|
|
if (!storageKey) return;
|
|
const blob = asset.kind === "image" ? await getImageBlob(storageKey) : await getMediaBlob(storageKey);
|
|
if (!blob) return;
|
|
const path = `files/${safeFileName(storageKey)}.${fileExtension(blob.type, asset.kind)}`;
|
|
files.push({ storageKey, path, mimeType: blob.type || asset.data.mimeType, bytes: blob.size });
|
|
zipFiles.push({ name: path, data: blob });
|
|
}),
|
|
);
|
|
|
|
const data: AssetExportFile = { app: "infinite-canvas", version: 1, exportedAt: new Date().toISOString(), assets, files };
|
|
const zip = await createZip([{ name: "assets.json", data: JSON.stringify(data, null, 2) }, ...zipFiles]);
|
|
saveAs(zip, "我的素材.zip");
|
|
}
|
|
|
|
export async function readAssetPackage(file: File) {
|
|
const zip = await readZip(file);
|
|
const assetFile = zip.get("assets.json");
|
|
if (!assetFile) throw new Error("missing assets.json");
|
|
const data = JSON.parse(await assetFile.text()) as AssetExportFile;
|
|
await Promise.all(
|
|
data.files.map(async (item) => {
|
|
const blob = zip.get(item.path);
|
|
if (!blob) return;
|
|
const typedBlob = blob.type ? blob : blob.slice(0, blob.size, item.mimeType);
|
|
await (item.storageKey.startsWith("image:") ? setImageBlob(item.storageKey, typedBlob) : setMediaBlob(item.storageKey, typedBlob));
|
|
}),
|
|
);
|
|
return data.assets;
|
|
}
|
|
|
|
function safeFileName(value: string) {
|
|
return value.replace(/[\\/:*?"<>|]/g, "_");
|
|
}
|
|
|
|
function fileExtension(mimeType: string, kind: Asset["kind"]) {
|
|
if (mimeType.includes("png")) return "png";
|
|
if (mimeType.includes("jpeg")) return "jpg";
|
|
if (mimeType.includes("webp")) return "webp";
|
|
if (mimeType.includes("gif")) return "gif";
|
|
if (mimeType.includes("mp4")) return "mp4";
|
|
if (mimeType.includes("webm")) return "webm";
|
|
return kind === "image" ? "png" : "bin";
|
|
}
|