import { saveModelBinary } from "./modelBinaryStore"; const LOCAL_MODEL_EXTENSION_RE = /\.(fbx|obj|glb|gltf)$/i; export interface LocalModelFileResult { id: string; fileName: string; name: string; url: string; /** GLB/GLTF 二进制在 IndexedDB 中的存储 id;存在时运行时从 IndexedDB 还原 objectURL。 */ binaryId?: string; } function readFileAsDataUrl(file: File) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.addEventListener("load", () => { if (typeof reader.result === "string") { resolve(reader.result); return; } reject(new Error("模型文件读取失败")); }); reader.addEventListener("error", () => reject(reader.error ?? new Error("模型文件读取失败"))); reader.readAsDataURL(file); }); } /** * 读取本地模型文件,返回可在 / useLoader 中直接使用的 url。 * * - FBX / OBJ:沿用既有逻辑,读成 data URL(与旧工程兼容,只改了扩展名正则)。 * - GLB / GLTF:读成二进制 Blob 存入 IndexedDB(避免 base64 撑爆 localStorage), * 同时返回当前会话可用的 objectURL 与 binaryId;若 IndexedDB 不可用则安静退回 data URL。 */ export async function readLocalModelFile(file: File): Promise { if (!LOCAL_MODEL_EXTENSION_RE.test(file.name)) { throw new Error("当前仅支持 FBX / OBJ / GLB / GLTF 模型文件"); } const isGltfFamily = /\.glb$/i.test(file.name) || /\.gltf$/i.test(file.name); if (isGltfFamily) { const buffer = await file.arrayBuffer(); const blob = new Blob([buffer], { type: file.type || "model/gltf-binary" }); const id = crypto.randomUUID(); try { await saveModelBinary(id, blob); } catch { // IndexedDB 不可用时退回 data URL:当前会话仍可用,只是跨刷新持久化会失效。 } return { id, fileName: file.name, name: file.name.replace(LOCAL_MODEL_EXTENSION_RE, ""), url: URL.createObjectURL(blob), binaryId: id, }; } const dataUrl = await readFileAsDataUrl(file); return { id: crypto.randomUUID(), fileName: file.name, name: file.name.replace(LOCAL_MODEL_EXTENSION_RE, ""), url: dataUrl, }; }