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
3 Answers
Reset to default 5 +500Most 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.
- I've copied your
app.js
andindex.html
. - 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 - 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'))
- And I've been able to download
rook.amf
withAMFLoader
. No idea how you've managed to get the errorTHREE.AMFLoader: Error loading AMF - no AMF document found.
you've mentioned in ments. - But then I get
THREE.AMFLoader: jszip missing and file is pressed.
Please note the warning in the console
Maybe you should try to mimic this updated example https://threejs/examples/webgl_loader_amf.html which I, kind of, did...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.
- I've downloaded
jszip.module.min.js
from the aforementioned example. And placed it in the/models
too, just for simplicity. And added to theindex.html
as
But you don't need this part if you'll use the updated version of example with<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(); ...
es6
modules or if you'll downloadnon-es6
version ofjszip
- Now I can see the rook on the screen!