fix: fix upload bug 3d

This commit is contained in:
2025-10-20 20:00:28 +03:00
parent f5142ec95d
commit 1b8fc3d215
9 changed files with 722 additions and 9 deletions

View File

@@ -3,6 +3,78 @@ import { OrbitControls, Stage, useGLTF } from "@react-three/drei";
import { useEffect, Suspense } from "react";
import { Box, CircularProgress, Typography } from "@mui/material";
// Утилита для очистки кеша GLTF
const clearGLTFCache = (url?: string) => {
try {
if (url) {
// Если это blob URL, очищаем его из кеша
if (url.startsWith("blob:")) {
useGLTF.clear(url);
console.log("🧹 clearGLTFCache: Очистка blob URL из кеша GLTF", {
url,
});
} else {
useGLTF.clear(url);
console.log("🧹 clearGLTFCache: Очистка кеша для URL", { url });
}
} else {
// Очищаем весь кеш GLTF
useGLTF.clear();
console.log("🧹 clearGLTFCache: Очистка всего кеша GLTF");
}
} catch (error) {
console.warn("⚠️ clearGLTFCache: Ошибка при очистке кеша", error);
}
};
// Утилита для проверки типа файла
const isValid3DFile = (url: string): boolean => {
try {
const urlObj = new URL(url);
const pathname = urlObj.pathname.toLowerCase();
const searchParams = urlObj.searchParams;
// Проверяем расширение файла в пути
const validExtensions = [".glb", ".gltf"];
const hasValidExtension = validExtensions.some((ext) =>
pathname.endsWith(ext)
);
// Проверяем параметры запроса на наличие типа файла
const fileType = searchParams.get("type") || searchParams.get("format");
const hasValidType =
fileType && ["glb", "gltf"].includes(fileType.toLowerCase());
// Если это blob URL, считаем его валидным (пользователь выбрал файл)
const isBlobUrl = url.startsWith("blob:");
// Если это URL с токеном и нет явного расширения, считаем валидным
// (предполагаем что сервер вернет правильный файл)
const hasToken = searchParams.has("token");
const isServerUrl = hasToken && !hasValidExtension;
const isValid =
hasValidExtension || hasValidType || isBlobUrl || isServerUrl;
console.log("🔍 isValid3DFile: Проверка файла", {
url,
pathname,
hasValidExtension,
fileType,
hasValidType,
isBlobUrl,
isServerUrl,
isValid,
});
return isValid;
} catch (error) {
console.warn("⚠️ isValid3DFile: Ошибка при проверке URL", error);
// В случае ошибки парсинга URL, считаем валидным (пусть useGLTF сам разберется)
return true;
}
};
type ModelViewerProps = {
width?: string;
fileUrl: string;
@@ -10,6 +82,18 @@ type ModelViewerProps = {
};
const Model = ({ fileUrl }: { fileUrl: string }) => {
// Очищаем кеш перед загрузкой новой модели
useEffect(() => {
// Очищаем кеш для текущего URL
clearGLTFCache(fileUrl);
}, [fileUrl]);
// Проверяем валидность файла перед загрузкой (только для blob URL)
if (fileUrl.startsWith("blob:") && !isValid3DFile(fileUrl)) {
console.warn("⚠️ Model: Попытка загрузить невалидный 3D файл", { fileUrl });
throw new Error(`Файл не является корректной 3D моделью: ${fileUrl}`);
}
const { scene } = useGLTF(fileUrl);
return <primitive object={scene} />;
};
@@ -49,11 +133,26 @@ export const ThreeView = ({
height = "100%",
width = "100%",
}: ModelViewerProps) => {
// Очищаем кеш при размонтировании
// Проверяем валидность файла (только для blob URL)
useEffect(() => {
if (fileUrl.startsWith("blob:") && !isValid3DFile(fileUrl)) {
console.warn("⚠️ ThreeView: Невалидный 3D файл", { fileUrl });
}
}, [fileUrl]);
// Очищаем кеш при размонтировании и при смене URL
useEffect(() => {
// Очищаем кеш сразу при монтировании компонента
clearGLTFCache(fileUrl);
console.log("🧹 ThreeView: Очистка кеша модели при монтировании", {
fileUrl,
});
return () => {
useGLTF.clear(fileUrl);
console.log("🧹 ThreeView: Очистка кеша модели", { fileUrl });
clearGLTFCache(fileUrl);
console.log("🧹 ThreeView: Очистка кеша модели при размонтировании", {
fileUrl,
});
};
}, [fileUrl]);

View File

@@ -1,9 +1,10 @@
import { Box } from "@mui/material";
import { useState } from "react";
import { useState, useEffect } from "react";
import { ReactPhotoSphereViewer } from "react-photo-sphere-viewer";
import { ThreeView } from "./ThreeView";
import { ThreeViewErrorBoundary } from "./ThreeViewErrorBoundary";
import { clearMediaTransitionCache } from "../../shared/lib/gltfCacheManager";
export interface MediaData {
id: string | number;
@@ -28,6 +29,30 @@ export function MediaViewer({
}>) {
const token = localStorage.getItem("token");
const [resetKey, setResetKey] = useState(0);
const [previousMediaId, setPreviousMediaId] = useState<
string | number | null
>(null);
// Сбрасываем ключ при смене медиа
useEffect(() => {
if (media?.id !== previousMediaId) {
console.log("🔄 MediaViewer: Смена медиа, сброс ключа", {
previousMediaId,
newMediaId: media?.id,
mediaType: media?.media_type,
});
// Используем новый cache manager для очистки кеша
clearMediaTransitionCache(
previousMediaId,
media?.id || null,
media?.media_type
);
setResetKey(0);
setPreviousMediaId(media?.id || null);
}
}, [media?.id, media?.media_type, previousMediaId]);
const handleReset = () => {
console.log("🔄 MediaViewer: handleReset вызван", {