最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Cannot load file path for files (3D models) with Node.js Server and Three.js - Stack Overflow

programmeradmin1浏览0评论

I'm having difficulty loading 3D models from a folder on my puter, using a localhost node.js test server, with three.js library.

app.js: (I run it via mand line in the project directory using: node app.js mand)

var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
var THREE = require('three');

app.get('/', function(req, res) {
    res.sendFile(__dirname + '/index.html');
});
io.on('connection', (socket) => {
  console.log('a user connected');
});

http.listen(3000, () => {
  console.log('listening on *:3000');
});

Relevant portion of index.html:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Index.html title</title>

</head>

<body>
    
<script src="/socket.io/socket.io.js"></script> 
<script src="//threejs/build/three.js"></script> 
<script src="//threejs/examples/js/loaders/AMFLoader.js"></script>
<script src="//threejs/examples/js/controls/OrbitControls.js"></script>

<script>
    
    var socket = io();
    
    var camera, scene, renderer;

    init();

    function init() {
        scene = new THREE.Scene();
        scene.add( new THREE.AmbientLight( 0x999999 ) );
        camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 500 );
        camera.up.set( 0, 0, 1 );
        camera.position.set( 0, -9, 6 );
        camera.add( new THREE.PointLight( 0xffffff, 0.8 ) );
        scene.add( camera );
        var grid = new THREE.GridHelper( 25, 1.0, 0xffffff, 0x555555 );
        grid.rotateOnAxis( new THREE.Vector3( 1, 0, 0 ), 90 * ( Math.PI/180 ) );
        scene.add( grid );
        renderer = new THREE.WebGLRenderer( { antialias: true } );
        renderer.setClearColor( 0x999999 );
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( window.innerWidth, window.innerHeight );
        document.body.appendChild( renderer.domElement );
        
        /* everything up till here works */

        var loader = new THREE.AMFLoader(); 
        loader.load('/models/rook.amf', function ( amfobject ) { //'./models/rook.amf'

            scene.add( amfobject );
            render();

        } );

        /* ^^^ this is the part not working */

        var controls = new THREE.OrbitControls( camera, renderer.domElement );
        controls.addEventListener( 'change', render );
        controls.target.set( 0, 1.2, 2 );
        controls.update();
        window.addEventListener( 'resize', onWindowResize, false );

    }
    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize( window.innerWidth, window.innerHeight );
        render();
    }
    function render() {    
        renderer.render( scene, camera );
    }
</script>
</body>
</html>

My project directory:

The error:

GET http://localhost:3000/models/rook.amf 404 (Not Found)

How can I properly load files with Node and three.js on my simple server test?

I'm having difficulty loading 3D models from a folder on my puter, using a localhost node.js test server, with three.js library.

app.js: (I run it via mand line in the project directory using: node app.js mand)

var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
var THREE = require('three');

app.get('/', function(req, res) {
    res.sendFile(__dirname + '/index.html');
});
io.on('connection', (socket) => {
  console.log('a user connected');
});

http.listen(3000, () => {
  console.log('listening on *:3000');
});

Relevant portion of index.html:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Index.html title</title>

</head>

<body>
    
<script src="/socket.io/socket.io.js"></script> 
<script src="//threejs/build/three.js"></script> 
<script src="//threejs/examples/js/loaders/AMFLoader.js"></script>
<script src="//threejs/examples/js/controls/OrbitControls.js"></script>

<script>
    
    var socket = io();
    
    var camera, scene, renderer;

    init();

    function init() {
        scene = new THREE.Scene();
        scene.add( new THREE.AmbientLight( 0x999999 ) );
        camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 500 );
        camera.up.set( 0, 0, 1 );
        camera.position.set( 0, -9, 6 );
        camera.add( new THREE.PointLight( 0xffffff, 0.8 ) );
        scene.add( camera );
        var grid = new THREE.GridHelper( 25, 1.0, 0xffffff, 0x555555 );
        grid.rotateOnAxis( new THREE.Vector3( 1, 0, 0 ), 90 * ( Math.PI/180 ) );
        scene.add( grid );
        renderer = new THREE.WebGLRenderer( { antialias: true } );
        renderer.setClearColor( 0x999999 );
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( window.innerWidth, window.innerHeight );
        document.body.appendChild( renderer.domElement );
        
        /* everything up till here works */

        var loader = new THREE.AMFLoader(); 
        loader.load('/models/rook.amf', function ( amfobject ) { //'./models/rook.amf'

            scene.add( amfobject );
            render();

        } );

        /* ^^^ this is the part not working */

        var controls = new THREE.OrbitControls( camera, renderer.domElement );
        controls.addEventListener( 'change', render );
        controls.target.set( 0, 1.2, 2 );
        controls.update();
        window.addEventListener( 'resize', onWindowResize, false );

    }
    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize( window.innerWidth, window.innerHeight );
        render();
    }
    function render() {    
        renderer.render( scene, camera );
    }
</script>
</body>
</html>

My project directory:

The error:

GET http://localhost:3000/models/rook.amf 404 (Not Found)

How can I properly load files with Node and three.js on my simple server test?

Share Improve this question edited Aug 5, 2020 at 6:06 Toribio 4,0784 gold badges38 silver badges49 bronze badges asked Aug 1, 2020 at 19:14 JDSJDS 17k47 gold badges171 silver badges234 bronze badges 2
  • I think you need to set up express to handle static files? expressjs./en/starter/static-files.html – Jesse Aldridge Commented Aug 5, 2020 at 5:59
  • Could you please provide us a minimal reproducible example we can work on? Something like a tiny dedicated git repository. Thank you – Daniele Ricci Commented Aug 5, 2020 at 10:06
Add a ment  | 

3 Answers 3

Reset to default 5 +500

Most of the popular 3D formats extensions (.glb, .gltf, .fbx, .dae, .amf, ...) are not standard MIME types, browsers pay a particular care when manipulating these files, attempting to safeguard the user to prevent dangerous behaviors.

So you will need to configure your web server engine to accept these extensions, otherwise you'll receive different HTTP errors downloading them. .amf is not even in this list, so application/octet-stream is the default value for all other cases. An unknown file type should use this type.
If you are using IIS server from an ASP.Net application, add the xml lines below in the </system.webServer> node of your web.config file:

<system.webServer>
      ...
      <staticContent>
          <remove fileExtension=".mtl" />
          <mimeMap fileExtension=".mtl" mimeType="model/mtl" />
          <remove fileExtension=".obj" />
          <mimeMap fileExtension=".obj" mimeType="model/obj" />
          <remove fileExtension=".glb" />
          <mimeMap fileExtension=".glb" mimeType="model/gltf-binary" />
          <remove fileExtension=".gltf" />
          <mimeMap fileExtension=".gltf" mimeType="model/gltf+json" />
          <remove fileExtension=".fbx" />
          <mimeMap fileExtension=".fbx" mimeType="application/octet-stream" />
          <remove fileExtension=".amf" />
          <mimeMap fileExtension=".amf" mimeType="application/octet-stream" />
      </staticContent>
</system.webServer>

If you are using an nginx server, add the following lines to the nginx.conf file in the http object:

http {
    include /etc/nginx/mime.types;
    types {
        model/mtl mtl;
        model/obj obj;
        model/gltf+json gltf;
        model/gltf-binary glb;
        application/octet-stream fbx;
        application/octet-stream amf;
    }
    ...
}

If you are using an Apache server, add the following lines to the mime.types file:

model/mtl mtl
model/obj obj
model/gltf+json gltf
model/gltf-binary glb
application/octet-stream fbx
application/octet-stream amf

For any other web server you can surely find easily how to set the MIME Types configuration.

EDIT: In the case of node, review your file server.js is not including any restriction to these MIME types, most of the popular server.js scripts include limitations for non-standard MIME types

EDIT2: I GOT IT as I suspected it was related to the kind of MIME type AMF is. I have posted the full solution on GitHub

But the important things to change are these ones below.

First, change your app.js to add the MIME type.

const express = require('express');
const app = express();
const path = require('path');
const router = express.Router();

router.get('/', function (req, res) {
    res.sendFile(path.join(__dirname + '/index.html'));
    //__dirname : It will resolve to your project folder.
});

app.use('/models', express.static('models')) // add this!
app.use('/express', express.static('express')) // add this!
express.static.mime.define({ 'application/octet-stream': ['amf'] })
//add the router
app.use('/', router);
app.listen(process.env.port || 3000);

console.log('Running at Port 3000');

Second, because of the recent changes in Three.js (April 23), the use of AMF loader, requires of jszip module. No need to download it locally, you can consume it by url like the other js files. In my project, index.html is in a /express folder, so my path to amf model is ../models/rock.amf

<!DOCTYPE html>

<html xmlns="http://www.w3/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Index.html title</title>
</head>

<body>

    <script src="https://threejs/build/three.min.js"></script>
    <script src="https://threejs/examples/js/libs/jszip.min.js"></script>
    <script src="https://threejs/examples/js/loaders/AMFLoader.js"></script>
    <script src="https://threejs/examples/js/controls/OrbitControls.js"></script>

    <script>
        var camera, scene, renderer;

        init()

        function init() {
            scene = new THREE.Scene();
            scene.add(new THREE.AmbientLight(0x999999));
            camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500);
            camera.up.set(0, 0, 1);
            camera.position.set(0, -9, 6);
            camera.add(new THREE.PointLight(0xffffff, 0.8));
            scene.add(camera);
            var grid = new THREE.GridHelper(25, 1.0, 0xffffff, 0x555555);
            grid.rotateOnAxis(new THREE.Vector3(1, 0, 0), 90 * (Math.PI / 180));
            scene.add(grid);
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setClearColor(0x999999);
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            /* NOW IT WORKS!!! */

            var loader = new THREE.AMFLoader();
            loader.load('../models/rook.amf', function (amfobject) { //'./models/rook.amf'

                scene.add(amfobject);
                render();

            });

            /* ^^^ this is the part not working */

            var controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.addEventListener('change', render);
            controls.target.set(0, 1.2, 2);
            controls.update();
            window.addEventListener('resize', onWindowResize, false);

        }
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
            render();
        }
        function render() {
            renderer.render(scene, camera);
        }
    </script>
</body>
</html>

You got it...

You need to serve the contents of the models folder as static files. Example:

app.use('/models', express.static('models'))

Try to recreate these steps from the code in the question. And you'll succeed too. Maybe there is something else in your real code that is missing in the question. Then you should update it with this info.

  1. I've copied your app.js and index.html.
  2. I've loaded rook.amf from https://threejs/examples/models/amf/rook.amf and placed it in the /models. I suppose you did the same
  3. I've added app.use() as others already correctly suggested.
    const express = require('express')
    const app = express()
    const http = require('http').createServer(app)
    // const io = require('socket.io')(http) // unrelated
    // const THREE = require('three') // unrelated
    app.use('/models', express.static('models')) // add this!
    app.get('/', function(req, res) { res.sendFile(__dirname + '/index.html') })
    // io.on('connection', (socket) => console.log('a user connected')) // unrelated
    http.listen(3000, () => console.log('listening on *:3000'))
    
  4. And I've been able to download rook.amf with AMFLoader. No idea how you've managed to get the error THREE.AMFLoader: Error loading AMF - no AMF document found. you've mentioned in ments.
  5. But then I get THREE.AMFLoader: jszip missing and file is pressed. Please note the warning in the console
    THREE.AMFLoader: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs/docs/#manual/en/introduction/Installation.
    
    Maybe you should try to mimic this updated example https://threejs/examples/webgl_loader_amf.html which I, kind of, did...
  6. I've downloaded jszip.module.min.js from the aforementioned example. And placed it in the /models too, just for simplicity. And added to the index.html as
    <script type="module">
      import { JSZip } from "./models/jszip.module.min.js"
      window.JSZip = JSZip
    </script>
    ...
    <!-- I also had to add type="module" to the "main" script
         so it would be executed after window.JSZip = JSZip -->
    <script type="module">
      var socket = io();
      ...
    
    But you don't need this part if you'll use the updated version of example with es6 modules or if you'll download non-es6 version of jszip
  7. Now I can see the rook on the screen!
发布评论

评论列表(0)

  1. 暂无评论