node.jscreatewritestream

How to write data into file in node JS?


I have these excel file contains columns : No, itemCode, startAt, endAt

I loop the data using createReadStream

And I want to detect the invalid datetime from excel file and write the error into test.txt file

Here's my current code

const csv = require('csv-parser');
const fs = require('fs');
const moment = require('moment');
const yesql = require('yesql');
const _ = require('lodash');

const { exit } = require('process');

async function run() {

    fs.createReadStream('test.csv')
        .pipe(csv())
        .on('data', (data) => csvResults.push(data))
        .on('end', async () => { 
         
            const groups = _.groupBy(csvResults, item => item['itemCode']);
            for (const itemCode in groups) {

                for (const item of groups[itemCode]) {
                    const startDateTime = moment(item['startAt'], 'YYYY-MM-DD HH:mm:ss');
                    const endDateTime = moment(item['endAt'], 'YYYY-MM-DD HH:mm:ss');
                    if (startDateTime.isAfter(endDateTime)) {
                        console.error(`Datetime invalid ${item['No']}`); 

                        // Want to write into file if got an error
                        var stream = fs.createWriteStream("test.txt");
                            stream.once('open', function(fd) {
                            stream.write(`${item['No']} : ${startDateTime} ${endDateTime} \n`); 
                            stream.end();
                        });

                        continue;
                    }  
                }
            }
            exit();
        });
}

run();

Solution

  • Your for loop does NOT wait for the fs.createWriteStream() stuff to complete so you end up trying to write to the same file multiple times at the same time which creates a mess in that file. Likewise, you call exit() before your writes are even done also because the for loop doesn't wait for the stream to finish.

    If what you want to do is to append each error condition to your file, then you can do it like this:

    const csv = require('csv-parser');
    const fs = require('fs');
    const fsp = fs.promises;
    const moment = require('moment');
    const yesql = require('yesql');
    const _ = require('lodash');
    
    const { exit } = require('process');
    
    async function run() {
    
        fs.createReadStream('test.csv')
            .pipe(csv())
            .on('data', (data) => csvResults.push(data))
            .on('end', async () => { 
    
                    const groups = _.groupBy(csvResults, item => item['itemCode']);
                    for (const itemCode in groups) {
    
                        for (const item of groups[itemCode]) {
                            try {
                                const startDateTime = moment(item['startAt'], 'YYYY-MM-DD HH:mm:ss');
                                const endDateTime = moment(item['endAt'], 'YYYY-MM-DD HH:mm:ss');
                                if (startDateTime.isAfter(endDateTime)) {
                                    console.error(`Datetime invalid ${item['No']}`); 
    
                                    // append into file if got an error
                                    await fsp.appendFile("test.txt", `${item['No']} : ${startDateTime} ${endDateTime} \n`);
                                }  
                            } catch(e) {
                                console.log(e);
                                // not sure what you want to do here if you got an error while appending to the file
                            }
                        }
                    }
                    exit();
            });
    }
    
    run();
    

    If you want to just record the first error in the file and then stop further processing, you can use fsp.writeFile() instead of fsp.appendFile() and put your call to exit() right after that call.