You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Grafana面板插件使用ThreeJS加载.gltf文件出现404错误求助

在Grafana面板插件中加载ThreeJS GLTF模型的404问题解决

问题概述

开发Grafana面板插件时,尝试通过ThreeJS加载.gltf模型文件,已将模型置于src文件夹并在src/components/SimplePanel.tsx中引用,但始终出现GET请求404错误。Three依赖已正确安装,面板能显示Three默认黑屏,核心问题为资源路径或打包配置错误。

错误日志

threejs-panel  | logger=context userId=0 orgId=1 uname= t=*** level=info msg="Request Completed" method=GET path=/api/live/ws status=-1 remote_addr=172.18.0.1 time_ms=0 duration=693.968µs size=0 referer= handler=/api/live/ws

当前代码与配置

SimplePanel.tsx

import React, { useEffect, useRef } from 'react';
import { PanelProps } from '@grafana/data';
import { SimpleOptions } from 'types';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

interface Props extends PanelProps<SimpleOptions> {}

export const SimplePanel: React.FC<Props> = ({ options, data, width, height }) => {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer();
    const loader = new GLTFLoader();

    renderer.setSize(width, height);
    containerRef.current.appendChild(renderer.domElement);

    loader.load('./src/Blackhawk.gltf', (gltf) => {
      const model = gltf.scene;
      model.scale.set(0.1, 0.1, 0.1);
      scene.add(model);

      // Adjust camera position
      const box = new THREE.Box3().setFromObject(model);
      const size = box.getSize(new THREE.Vector3());
      const center = box.getCenter(new THREE.Vector3());

      const maxDim = Math.max(size.x, size.y, size.z);
      const fov = camera.fov * (Math.PI / 180);
      let distance = maxDim / (2 * Math.tan(fov / 2));
      distance += maxDim * 0.25;

      camera.position.copy(center);
      camera.position.z += distance;
      camera.lookAt(center);

      // Optional: Add lighting to the scene
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
      scene.add(ambientLight);

      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
      directionalLight.position.set(5, 5, 5);
      scene.add(directionalLight);

      function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
      }

      animate();
    });
  }, []);

  return (
    <div
      ref={containerRef}
      style={{ width: '100%', height: '100%', overflow: 'hidden' }}
    ></div>
  );
};

Webpack.config.ts相关片段

test: /\.(png|jpe?g|gif|svg|gltf)$/,
type: 'asset/resource',
generator: {
  // Keep publicPath relative for host.com/grafana/ deployments
  publicPath: `public/plugins/${getPluginId}/img/`,
  outputPath: 'img/',
  filename: Boolean(env.production) ? '[hash][ext]' : '[name][ext]',
},

遇到的额外问题

  • 运行dev脚本时,.gltf文件未被打包到dist文件夹
  • 曾成功打包出两个文件:dist/img/2efghij3***.gltf和dist/9dbql***.gltf(后者引用前者),但仍无法加载

解决步骤

1. 调整资源存放位置

将.gltf模型及关联资源(如.bin、纹理图)移至插件根目录的public/img文件夹。Grafana插件默认从public目录加载静态资源,此位置能确保Webpack正确识别并打包资源。

2. 更新Webpack配置

添加对.bin文件的支持(GLTF模型常包含二进制数据),修改规则如下:

test: /\.(png|jpe?g|gif|svg|gltf|bin)$/,
type: 'asset/resource',
generator: {
  publicPath: `public/plugins/${getPluginId}/img/`,
  outputPath: 'img/',
  filename: Boolean(env.production) ? '[hash][ext]' : '[name][ext]',
},

3. 修正模型加载路径

使用Grafana提供的getPluginUrl方法获取插件基础URL,避免硬编码相对路径:

  • 首先导入方法:
    import { getPluginUrl } from '@grafana/runtime';
    
  • 修改loader.load的路径:
    const modelUrl = `${getPluginUrl()}/img/Blackhawk.gltf`;
    loader.load(modelUrl, (gltf) => {
      // 原有模型处理逻辑保持不变
    });
    

4. 重新构建与验证

  • 删除dist文件夹,清理旧构建产物
  • 运行dev或build命令,确认模型及关联资源被打包到dist/img目录
  • 重启Grafana容器,刷新面板验证模型是否加载成功

内容的提问来源于stack exchange,提问作者zebedee freeman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 07:05:55