Search code examples
javascriptnode.jsexpressazure-web-app-servicehtml-pdf

HTML to PDF file converter API works in local environment but not in deployment (node.Js,Express.Js,html-pdf,Azure Web Services)


I have built an HTML to PF converter API which works perfectly in the local environment but not in production with Azure app web services.

The request gets stuck while trying to generate the PDF and fails because of timeout.

Here is my code:

const pdf = require('html-pdf');
var fs = require('fs');

const options = {
    "height": "14cm",
    "width": "20cm",
    "border": "0",
    "header": {
        "height": "0mm",
    },
    "footer": {
        "height": "0mm",
    }
}
function generatePDF(req, res) {
    const htmlContent = req.body.html;
    try {
    return new Promise((resolve, reject) => {
        pdf.create(htmlContent, options).toFile("./public/docs/doc.pdf", (err, res) => {
                if (err) {
                    console.log(err);
                    res.status(500).send("Une erreur s'est produite lors de la creation du fichier Pdf")
                    resolve();
                } else {
                    resolve();
                    console.log("Le fichier Pdf a été généré dans le serveur");
                }
            });
    });
    } catch (err) {
            res.status(500).send("Le fichier Pdf n'a pas pu etre généré pour un raison quelconque")
            resolve();
        }
}

function readPDF(req, res) {
    try {
        fs.readFile('./public/docs/doc.pdf', (err, data) => {
            if (err) {
                console.log(err);
                res.status(500).send("Une erreur s'est produite lors de la lecture du fichier")
                return;
            } else {
                //Convertir le contenu binaire (buffer) en Base64
                const base64String = data.toString('base64');
                //console.log(base64String);
                res.status(200).send(base64String);
            }
        })
    } catch (err) {
        res.status(500).send("Le fichier pdf n'a pas etre lu (ficher introuvable ou inexistant)")
        console.log("Le fichier pdf n'a pas etre lu (ficher introuvable ou inexistant)");
    }
}

exports.generatePDFfromHTML = async function (req, res) {
    await generatePDF(req, res)
    readPDF(req, res); 
}

I am deploying it directly from VS code using the Azure Web app services extension.

Here is the error I am getting on the server:

htmltopdfconverterapi2.azurewebsites.net 200 0 0 1522 1136 6Error: html-pdf: PDF generation timeout. Phantom.js script did not exit.at Timeout.execTimeout (C:\home\site\wwwroot\node_modules\html-pdf\lib\pdf.js:93:19)at listOnTimeout (node:internal/timers:559:17)at processTimers (node:internal/timers:502:7)Thu May 30 2024 13:27:27 GMT+0000 (Coordinated Universal Time): Application has thrown an uncaught exception and is terminated:TypeError: Cannot read properties of undefined (reading 'status')at C:\home\site\wwwroot\controller\convert.controller.js:22:21at respond (C:\home\site\wwwroot\node_modules\html-pdf\lib\pdf.js:141:14)at Timeout.execTimeout (C:\home\site\wwwroot\node_modules\html-pdf\lib\pdf.js:93:5)at listOnTimeout (node:internal/timers:559:17)at processTimers (node:internal/timers:502:7)Application has thrown an uncaught exception and is terminated:TypeError: Cannot read properties of undefined (reading 'status')at C:\home\site\wwwroot\controller\convert.controller.js:22:21at respond (C:\home\site\wwwroot\node_modules\html-pdf\lib\pdf.js:141:14)at Timeout.execTimeout (C:\home\site\wwwroot\node_modules\html-pdf\lib\pdf.js:93:5)at listOnTimeout (node:internal/timers:559:17)at processTimers (node:internal/timers:502:7)

The error seems to come from the html-pdf library which is using PhantomJs as Browser Engine


Solution

  • The request gets stuck while trying to generate the PDF and fails because of timeout.

    • Consider upgrading the service plan due to resource limitations may affect PDF generation on Azure Web App.
    • Switch to Puppeteer from PhantomJS for improved compatibility and maintenance.

    I tried your code but received the same error, so I added Puppeteer to your code to handle the PDF generation process more smoothly.

    I made the generatePDF function asynchronous by adding the async keyword, ensuring the PDF generation completes before reading.

    This is my index.js:

    const express = require('express');
    const puppeteer = require('puppeteer');
    const fs = require('fs');
    const path = require('path');
    const pdf = require('html-pdf');
    const app = express();
    const port = process.env.PORT || 8080;
    app.use(express.json({ limit: '10mb' }));
    app.use('/docs', express.static(path.join(__dirname, 'public', 'docs'))); 
    const options = {
        "height": "14cm",
        "width": "20cm",
        "border": "0",
        "header": {
            "height": "0mm",
        },
        "footer": {
            "height": "0mm",
        }
    };
    async function generatePDF(htmlContent) {
        const browser = await puppeteer.launch({
            args: ['--no-sandbox', '--disable-setuid-sandbox']
        });
        const page = await browser.newPage();
        await page.setContent(htmlContent, { waitUntil: 'networkidle0' });
        await page.pdf({ path: './public/docs/doc.pdf', ...options });
        await browser.close();
        console.log("PDF file has been generated on the server");
    }
    function readPDF(req, res) {
        fs.readFile('./public/docs/doc.pdf', (err, data) => {
            if (err) {
                console.log(err);
                res.status(500).send("An error occurred while reading the file");
            } else {
                const base64String = data.toString('base64');
                console.log('PDF read successfully');
                res.status(200).send(base64String);
            }
        });
    }
    app.post('/generate-pdf', async (req, res) => {
        const htmlContent = req.body.html;
        try {
            await generatePDF(htmlContent);
            readPDF(req, res); 
        } catch (err) {
            console.error("Error generating PDF:", err);
            res.status(500).send("An error occurred while generating the PDF file");
        }
    });
    
    app.get('/', (req, res) => {
        res.send('Welcome to the PDF generation service');
    });
    app.listen(port, () => {
        console.log(`Server is running on port ${port}`);
    });
    

    local output:

    enter image description here

    I deployed the app successfully via visual studio code extension.

    Here's the output after deployment:

    enter image description here