Search code examples
node.jspdfmake

PDF download fails using PDFMake in NodeJS


I added to my NodeJS API an endpoint to be able to generate and download a PDF using the library PDFMake. I'm able to generate and upload the PDF on the server-side but on browser site, the PDF is downloaded and failed with 0 KBytes on it and I stack trying to find a solution for it. I understood that the file is downloaded before the writeStream finish to write it but adding an extra function as like:

pdf.on("finish", async () => {
            res.download(pdf);
            res.send("PDF generated");
        });

This didn't help but added an extra error of:

UnhandledPromiseRejectionWarning: RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: undefined

I have no idea how to solve this and hope for help. Most probably is something I'm missing.

The code responsible for the functionalities are: Router

router.get("/pdf/all", async (req, res) => {
    const movies = await movie.getMovies();
    try {
        const pdf = await generatePdf(movies, "all");
        pdf.on("finish", async () => {
            res.download(pdf);
            res.send("PDF generated");
        });
    } catch (err) {
        // Errors
        res.status(err.status).json({ message: err.message });
        throw new Error(error.message);
    }
});

generatePdf

const generatePdf = (movies, name) => {
    return new Promise((resolve, reject) => {
        try {
            let fonts = {
                Roboto: {
                    normal: "Helvetica",
                    bold: "Helvetica-Bold",
                    italics: "Helvetica-Oblique",
                    bolditalics: "Helvetica-BoldOblique"
                }
            };
            let printer = new pdfMaker(fonts);
            const pdfTemplate = template(movies);
            const pdfStream = printer.createPdfKitDocument(pdfTemplate, {});
            const filePath = path.join(
                __dirname,
                uploads + "/" + pdfDir + `${name}.pdf`
            );
            console.log(filePath);
            pdfStream.pipe(writeStream(filePath));
            pdfStream.end();
            resolve(filePath);
        } catch (err) {
            console.log(err);
            reject(err);
        }
    });
};

Solution

  • I would suggest you try the following code for generatePdf:

    const generatePdf = (movies, name) => {
        return new Promise((resolve, reject) => {
            try {
                let fonts = {
                    Roboto: {
                        normal: "Helvetica",
                        bold: "Helvetica-Bold",
                        italics: "Helvetica-Oblique",
                        bolditalics: "Helvetica-BoldOblique"
                    }
                };
                let printer = new pdfMaker(fonts);
                const pdfTemplate = template(movies);
                const pdfStream = printer.createPdfKitDocument(pdfTemplate, {});
                const filePath = path.join(
                    __dirname,
                    uploads + "/" + pdfDir + `${name}.pdf`
                );
                console.log(filePath);
                let stream = pdfStream.pipe(writeStream(filePath));
                stream.on('finish', function(){
                    pdfStream.end();
                    resolve(filePath);
                }
            } catch (err) {
                console.log(err);
                reject(err);
            }
        });
    };
    

    The code should wait for the pdfStream.pipe to finish before finishing the file and resolving the filePath

    Also you cannot use res.send after res.download as the download sets the response to be the file and you can no longer send another response to the client.