Jeremy
Jeremy

Reputation: 119

Node JS - createWriteStream

I am going crazy trying to fix this bug so please help :-) I am using https://pdfkit.org/

This creates a stream that when finished is piped to fs.createWriteStream

My issue is the first time the code runs this works and the PDF is generated. The next time the Code runs a file with Zero Bytes is created. I am calling the function from an API running on express. The issue appears to be the async nature of fs.createWriteStream. The stream finishes after the API has returned. I cannnot seem to find a way to block while confirming the file has been created.

What is odd is that the first time the code works run again it fails:

Here is the Pipe Function;

async function _writeFile(fileObj) {

    let fileStream = fs.createWriteStream(fileObj.fileName)

    pipeline(
        doc,
        fileStream,
        async (err) => {
            if (err) {
                console.error('PDF failed', err);
                return ('Pipeline failed', err)
            } else {
                console.log('PDF succeeded');

            }
        }

    )

}

This is called from:

exports.drawReport = async (payload) => {

    var date = new Date();
    const timeStamp = date.toJSON();
    let path = './controllers/tmp/'
    var fileName = path + timeStamp + '.' + payload.type + '.pdf'

    try {

        // Start Report 
        await _startReport(payload)

        // Check Starting position on page & add status box header
        if (device_card_reference == 260) {
            await _deviceTitle(payload);
        }

        // Add Devices
        await _reportDevice(payload);

        // Call Footer for final page
        await _reportFooter()

        console.log("PDF Done - Writing File")

        // File Meta Data
        let fileObj = {
            type: payload.type,
            siteId: payload.siteId,
            fileName: fileName,
            timeStamp: timeStamp

        }

        // Create file to store PDF
        await _writeFile(fileObj)

        doc.end()
        console.log("PDF MADE?")

        return (fileObj)


    } catch (err) {
        console.error('MakePDF ERROR: ' + err.message);
        return (err.message)
    }

}

Upvotes: 0

Views: 13040

Answers (1)

traynor
traynor

Reputation: 8717

pipeline runs asynchronously, so it's not awaited, which is why doc.end() runs before the file is done

try wrapping pipeline in a promise, and then resolve when the stream is done:

// function that returns a promise
function _writeFile(fileObj) {

    return new Promise((resolve, reject) => {

        const fileStream = fs.createWriteStream(fileObj.fileName);

        pipeline(
            doc,
            fileStream,
            async(err) => {
                if (err) {
                    console.error('PDF failed', err);
                    // err, handle in `.catch`
                    reject({res:'Pipeline failed', err});
                } else {
                    console.log('PDF succeeded');
                    // done, resolve, to move to doc.end
                    resolve('PDF succeeded');

                }
            }

        )
    });
}

add .catch() to handle error:

// Create file to store PDF
await _writeFile(fileObj).catch(err => console.log(err));

or even better, use stream promises API

const {pipeline } = require('stream/promises');

async function _writeFile(fileObj) {

    const fileStream = fs.createWriteStream(fileObj.fileName);

    await pipeline(doc, fileStream);

    console.log('PDF succeeded');

}

Upvotes: 0

Related Questions