How do I call loader function inside app.post in Node.js?

I am trying to create a node app in which I need to calculate the volume of the stl file which is uploaded by the user. I am using express and multer to handle the file uploading process. I am using three.js to calculate the volume of the stl file. However, when I add the volume code inside app.get(), it doesn’t work. I have tried the code on plain js and it works perfectly and outputs the volume. I am not sure if the code gets executed when running on node as nothing gets printed in the terminal console. I need this code to work on the server side as I don’t want the client to see this code and manipulate it. Please help me with this. Here is my current code:

app.get(’/’, function (req, res) {
console.log(“Hello!”); //This line gets printed
res.render(‘pages/index’);
});

app.post(’/upload’, upload.single(‘stl_file’), (req, res) => { //stl_file is the name attribute of input tag in html
var loader = new STLLoader();
loader.load(req.file, function (geometry) {
Code to calculate volume
console.log("stl volume is " + volume); //This line does not get printed
Code to calculate size
let stlSize = size;
console.log(stlSize); //This line does not get printed
});
return res.json({ status: ‘OK’});
});

app.listen(3001);

i don’t think you can use loaders in node because they rely on web apis like XHRRequests. but you could get lucky by trying to parse the model instead. some loaders parse functions work in node: three.js/STLLoader.js at 6b1cf0a5478d0844ae0062d74535adc25e65c385 · mrdoob/three.js · GitHub

for this to work you need the raw arraybuffer and then feed it to it:

new STLLoader().parse(buffer)

ps. i haven’t tried it, but if you run into problems ragarding DataView and node array buffers, you can most likely convert them. i’ve found this issue for instance: javascript - Using DataView with nodejs Buffer - Stack Overflow

1 Like

@drcmda thanks for the answer. Could me help me with the code? I am new to three.js and still cannot figure out what exactly needs to be done. Thank you.

i did, the code above parses an existing arraybuffer. Loader().load fetches a file first via xhrrequest and then parses. so by using parse you avoid the fetching. you can load a file as an arraybuffer using the node api.

I am currently trying to parse the file using this code but am getting a error:

"RangeError: Offset is outside the bounds of the DataView"

var enc = new TextEncoder();
    var arrayBuffer = enc.encode(req.file).buffer;
    console.log(arrayBuffer);
    var loader = new STLLoader();
    loader.parse(arrayBuffer, function (geometry) {
            Code to calculate volume
console.log("stl volume is " + volume); //This line does not get printed
Code to calculate size
let stlSize = size;
console.log(stlSize); //This line does not get printed
});
return res.json({ status: ‘OK’});
});
    });

can’t help you with that. if stl is binary you probably don’t need textencoder. i think i’d just try fs.readFileSync(myFile, null).buffer;

I was working on an Electron app with React that loads STL files from a local drive.

In Electron’s Main thread I’ve loaded files like this:

...
data = fs.readFileSync('/Users/luka/Downloads/cube.stl', 'ascii');
...

Then through IPC (two-way communication between Main and Renderer threads), I’ve returned data to Renderer thread and used STLLoader’s parse() method to load the data into the scene like this:

...
  useEffect(() => {
    const stlLoader = new STLLoader();
    loadStl().then((data) => {
      const geo = stlLoader.parse(data);
      setGeometry(geo);
    });
  }, []);
...

JSX part is:

<Canvas>
     <mesh geometry={geometry}>
          <meshStandardMaterial color="#cccccc" />
     </mesh>
</Canvas>

you can also integrate it into suspense, which would allow that component to interop with everything else, and the eco system. this is what all r3f loaders do, they all use suspend-react.

import { suspend } from 'suspend-react'

function Model({ loadStl, ...props }) {
  const geo = suspend(async () => new STLLoader().parse(await loadStl()), [loadStl])
  return (
    <mesh geometry={geo} {...props}>
      <meshStandardMaterial />
    </mesh>
  )
}

for instance, if you pair it with any interop component, like drei/center, it needs to know when loading has finished. it executes bounds calculation in useLayoutEffect. without suspense uLE will fire immediately after render, but now it will fire once your STL is there:

<Center>
  <Model loadStl={foo} />
</Center>

you can also re-use the model now because the data is cached.

@drcmda thank you for that! Pretty neat trick.

Do you know by any chance why my STLs are deformed after I load them? It only happens with STLs that I have created myself using Blender.

STLs from the Internet are loading just fine. Is there any param that I need to set to avoid such issues?

Thank you

i wouldn’t know. maybe blenders exporter is broken.