alixaxel / chrome-aws-lambda

Chromium Binary for AWS Lambda and Google Cloud Functions
MIT License
3.21k stars 293 forks source link

[BUG] PDF generation fails with blank error #160

Open mithundas79 opened 4 years ago

mithundas79 commented 4 years ago

Environment

Expected Behavior

Should create pdf

Current Behavior

I get error

{  error: {} }

Steps to Reproduce

import { APIGatewayProxyHandler } from 'aws-lambda';
import 'source-map-support/register';

import * as handlebars from 'handlebars';
import * as AWS from 'aws-sdk';
import chromium from "chrome-aws-lambda";
import puppeteer from "puppeteer-core";

const createHtml = async (template) => {

  const s3 = new AWS.S3();
    let Bucket = process.env.TEMPLATES_BUCKET;
    let Key = `proposal_template.html`;
    let { Body } = await s3.getObject({ Bucket, Key }).promise()
      .catch(e => Promise.reject(e));

    let html = Body.toString();

    html = handlebars.compile(html)(template.proposal);

    return html;

}

const htmlToPdf = async (html) => {
  let browser: any = null;
  let pdfBuffer: Buffer | null = null;

  try {
    browser = await chromium.puppeteer.launch({
      args: chromium.args,
      defaultViewport: chromium.defaultViewport,
      executablePath: await chromium.executablePath,
      headless: true,
      ignoreHTTPSErrors: true,
    })

    const page = await browser.newPage();
    await page.setContent(html);

    pdfBuffer = await page.pdf({
      format: "A4"
    });
  } catch (error) {
    throw error
  } finally {
    if (browser !== null) {
      await browser.close();
    }
  }
  return pdfBuffer;

}
const uploadToS3 = async (pdfBuffer: Buffer, BuketKey) => {
    const s3SecI = new AWS.S3();
    let Bucket = process.env.PROPOSALS_BUCKET;
    let Key = `${BuketKey}.pdf`;
    const s3Data = await s3SecI.upload({ Bucket, Key, Body: pdfBuffer, ACL: 'public-read' }).promise()
      .catch(e => Promise.reject(e));

    return s3Data.Location;
}
export const createHtmlToPdf: APIGatewayProxyHandler = async (event, _context) => {

  const requestBody = JSON.parse(JSON.stringify(event.body));

  const templateData = requestBody.templateData;

  return createHtml(templateData)
    .then((html) => {
      return htmlToPdf(html);
    })
    .then((pdf) => {
      const Key = `${templateData.proposalId}/${templateData.versionId}`;
      return uploadToS3(pdf, Key);
    })
    .then((pdfLink) => {
      return {
        statusCode: 200,
        body: JSON.stringify({
          pdfLink
        }, null, 2)
      };
    })
    .catch((err) => {
      return {
        statusCode: 200,
        body: JSON.stringify({
          message: 'Failed',
          error: err
        }, null, 2)
      };
    })
}

Possible Solution

I checked the createHtml, and uploadToS3 functions separately they works as normal.... but htmlPdf fails. Specifically it fails at

browser = await chromium.puppeteer.launch({
      args: chromium.args,
      defaultViewport: chromium.defaultViewport,
      executablePath: await chromium.executablePath,
      headless: true,
      ignoreHTTPSErrors: true,
    });
baharev commented 3 years ago

@mithundas79 If the problematic line is indeed what you are saying (browser = await chromium.puppeteer.launch({...) then most of the code you are showing us is likely to be irrelevant. My guess would be that you are having a configuration error / missing dependency, and you cannot launch the browser. That's all.

If that is not the case, please reduce your example because it has too many moving parts.

Hard-code the HTML as a plain string in the JavaScript code. Get rid of everything specific to the Lambda and write the PDF directly to disc by passing a path: '/tmp/myexample.pdf', to the await page.pdf({...}) call. We must be able to test everything locally and without sam.

If that still doesn't reveal where the problem is, try eliminating as much of the HTML and the JavaScript code as possible.

Long story short: Please try to make your example minimal.