mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 00:34:22 +08:00
74 lines
2.8 KiB
TypeScript
74 lines
2.8 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) => {
|
|
if (asset.kind !== "image" && asset.kind !== "video") return;
|
|
const storageKey = asset.data.storageKey;
|
|
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";
|
|
}
|