0

I'm quite new to this so please bare with me. I'm currently trying to put together an HTML report building tool. I have 2 html reports that are being generated by 3rd parties. I'd like to be able to upload them, parse them, save the specific parse to a variable and update my template which is in a folder on the server.

Currently, I'm using express, and node-html-parser.

I have no issues getting the HTML files uploaded to a directory on the server and parsing those files.

My issue comes in when I try to update the variable I want with the string that I want.

const fs = require('fs');
const htmlparse = require('node-html-parser').parse;

var element1
var element2

function datatoString(){
 fs.readFile(__dirname + "/api/upload/" + file1, 'utf8', (err,html)=>{

     const root = htmlparse(html);

     head = root.querySelector('head');
     element1 = head.toString();
 
     console.log("-------------break------------")
     console.log(head.toString()); //This works and shows me my parsed info
   
 });
 fs.readFile(__dirname + "/api/upload/" + file2, 'utf8', (err,html)=>{

    const root = htmlparse(html);

    body = root.querySelector('body');
    element2 = body.toString();
    console.log("-------------break------------")
    console.log(body.toString()); //This works and shows me my parsed info
 });
};

Now, ideally I'd like to call back this function in a GET request and have it update the variables. From there, I would use those strings to modify a template HTML file that's sitting in a folder on my server. I'd like to be able to replace html elements in the template with those updated variables. Once updated, id push the response to download the file. Every time I try this with a fs.writeFile , it seems to just say the variables 'element1' or 'element2' are empty. I'm not even sure if I can write a local HTML file and save it the same way you'd normally do it with the DOM.

I'm lost at this point. I would assume I'd need to read then write the template html file. but how i'd go about editing it, I have no clue. Also, the variables being empty is stumping me. I know it's due to the fact that fs.readFile is asynchronous, but then how would I go about reading and writing files in the manner I am looking for?

any help would be much appreciated!

  • Can you show the code where you call `datatoString()`? – skara9 Dec 06 '21 at 20:50
  • 1
    Does this answer your question? [How to return the response from an asynchronous call](https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) – Randy Casburn Dec 06 '21 at 20:52

2 Answers2

0

You have two possibilities: use fs.readFileSync, which is easy to use but since it 's synchronous, it blocks your thread (and makes your server unresponsive while the files are being read). The more elegant solution is to use the Promise version and to await it.

const promises = require('fs').promises;
const htmlparse = require('node-html-parser').parse;

let element1, element2;

async function datatoString() {

  let html = await promises.readFile(__dirname + "/api/upload/" + file1, 'utf8');
  let root = htmlparse(html);
  head = root.querySelector('head');
  element1 = head.toString();
  console.log("-------------break------------")
  console.log(element1);

  html = await promises.readFile(__dirname + "/api/upload/" + file2, 'utf8');
  root = htmlparse(html);
  body = root.querySelector('body');
  element2 = body.toString();
  console.log("-------------break------------")
  console.log(element2);
};
Jeremy Thille
  • 26,047
  • 12
  • 43
  • 63
  • If following this approach, I would maybe use something like `Promise.all` in order to run the reads simultaneously and improve speed. – skara9 Dec 06 '21 at 21:02
  • Yes I thought about it as well. But maybe it's not such a good idea after all, since we're reading from the disk. A disk can read only one file at once. – Jeremy Thille Dec 06 '21 at 21:04
  • no need to worry about that – skara9 Dec 06 '21 at 21:09
0

You have two options here. One is to block the thread and wait for each consecutive read to end before ending the function.

function datatoString() {
  let element1, element2;
  fs.readFileSync(... element1 = 'foo'});
  fs.readFileSync(... element2 = 'bar'});
  return [element1, element2];
}

app.get('/example', (req, res) => {
  ...
  const [element1, element2] = datatoString();
}

The other would be to use async and read both files at the same time, then return whenever they both finish:

function datatoString() {
  return new Promise((resolve, reject) => {
    let element1, element2;
    fs.readFile(... element1 = 'foo', if (element2) resolve([element1, element2]);});
    fs.readFile(... element2 = 'bar', if (element1) resolve([element1, element2]);});
  });
}

app.get('/example', async (req, res) => {
  ...
  const [element1, element2] = await datatoString();
}
skara9
  • 4,042
  • 1
  • 6
  • 21
  • `fs` offers a Promise version of its methods (readFile, writeFile), use `require('fs').promises` (see my answer). You don't need to wrap the non-Promise versions inside a Promise. – Jeremy Thille Dec 06 '21 at 20:57
  • @JeremyThille I'm aware, but fs promises lacks some features that the original module has. I generally prefer to use regular fs since you'll run into less issues. – skara9 Dec 06 '21 at 20:59
  • Also, I'm curious to see your solution with element3 :) Whenever one of the Promises resolve, will you test all possible combinations to know if the other ones have already resolved and the other values are defined? What about adding element4? Wouldn't it be simpler at this point to use Promise.all? – Jeremy Thille Dec 06 '21 at 21:03
  • @JeremyThille Yes, I just used this method since it's simpler when dealing with only 2 elements. If the number has to go up, you could either use `Promise.all`, or just use an object with keys instead of separate variables and check the length of the truthy values. – skara9 Dec 06 '21 at 21:06