Franco Berton - 20 Maggio, 2021

Generare pdf con aws lambda

Index

Introduzione: Questo articolo vuole essere una guida introduttiva su come generare un pdf con AWS Lambda in Node Js con la libreria Puppeteer.

Pre-requisiti: La seguente guida ha come prerequisito l'integrazione dei seguenti moduli:

  • serverless: framework volto alla creazione di applicazioni composte da microservizi e facilitare la distribuzione all'interno della piattaforma AWS;
  • serverless-bundle: plugin che impacchetta in modo ottimale le Lambda functions definite con ES6 o TypeScript basandosi sul plugin interno serverless-webpack;
  • serverless-offline: plugin per emulare AWS e Api Gateway sulla macchina locale per accelerare i cicli di sviluppo;
  • puppeteer-serverless, plugin fondamentale per la generazione del pdf con AWS Lambda. Internamente include come dipendenze: chrome-aws-lambda e puppeteer-core

Le dipendenze del nostro package.json si presenteranno in questo modo:

"dependencies": {
  "puppeteer-serverless": "^2.0.0"
},
"devDependencies": {
  "serverless": "^2.11.1",
  "serverless-bundle": "^4.0.1",
  "serverless-offline": "^6.8.0",
  "typescript": "^3.9.7"
}

1. Definizione microservizio nel file serverless

Il primo passo per la generazione di un pdf con AWS Lambda in NodeJs prevede la definizione di un microservizio all'interno del file serverless, il quale contiene la configurazione per il deployment.

Il microservizio sarà raggiungigibile all'esterno con una semplice api di get. Di seguito la definizione del file serverless:

service: pdf

plugins:
  - serverless-bundle
  - serverless-offline

package:
  individually: true

custom: 
  serverless-offline:
    location: .webpack/service
  bundle:
    sourcemaps: false  
provider: 
  name: aws
  runtime: nodejs12.x
  region: eu-central-1
  stage: test
  apiGateway: 
    shouldStartNameWithService: true
    binaryMediaTypes:
      - '*/*'
  tracing:
    apiGateway: true
    lambda: true
functions:
  downloadPdf:
    handler: lambdas/download-pdf.main
    events:
      - http:
          path: download-pdf
          method: get
          cors: true
    timeout: 180

2. Creazione della lambda function

A questo punto è necessario definire una lambda function per effettuare la magia. Il processo di generazione del pdf prevede:

  • definizione contenuto html da stampare;
  • caricamento e apertura del file html con puppeteer;
  • generazione del file pdf con puppeteer;
  • conversione del contenuto in base64.
import puppeteer from "puppeteer-serverless";

export const main = async (event: any, context: any): Promise<any> => {
    let browser = null;
    let pdf = null;

    try {
      browser = await puppeteer.launch({});
      const page = await browser.newPage();
      await page.setContent("<html><body><p>Test</p></body></html>", {
        waitUntil: "load",
      });

      pdf = await page.pdf({
        format: "A4",
        printBackground: true,
        displayHeaderFooter: true,
        margin: {
          top: 40,
          right: 0,
          bottom: 40,
          left: 0,
        },
        headerTemplate: `
          <div style="border-bottom: solid 1px gray; width: 100%; font-size: 11px;
                padding: 5px 5px 0; color: gray; position: relative;">
          </div>`,
        footerTemplate: `
          <div style="border-top: solid 1px gray; width: 100%; font-size: 11px;
              padding: 5px 5px 0; color: gray; position: relative;">
              <div style="position: absolute; right: 20px; top: 2px;">
                <span class="pageNumber"></span>/<span class="totalPages"></span>
              </div>
          </div>
        `,
      });
    } finally {
      if (browser !== null) {
        await browser.close();
      }
    }

  return {
    headers: {
      'Content-type': 'application/pdf',
      'content-disposition': 'attachment; filename=test.pdf'
    },
    statusCode: 200,
    body: pdf.toString('base64'),
    isBase64Encoded: true
  }
}

3. Inclusione nel bundle dei file binari di chromium

Come ultimo step andiamo ad aggiornare il file serverless con l'inclusione dei file binari di chromium nel bundle, affinchè il download del pdf possa funzionare su AWS

I file binari sono presenti all'interno del modulo chrome-aws-lambda e quindi, saranno:

  • node_modules/chrome-aws-lambda/bin/aws.tar.br
  • node_modules/chrome-aws-lambda/bin/chromium.br
  • node_modules/chrome-aws-lambda/bin/swiftshader.tar.br
service: pdf

plugins:
  - serverless-bundle
  - serverless-offline

package:
  individually: true

custom: 
  serverless-offline:
    location: .webpack/service
  bundle:
    sourcemaps: false  
    copyFiles:                       
      - from: 'node_modules/chrome-aws-lambda/bin/aws.tar.br'
        to: './bin' 
      - from: 'node_modules/chrome-aws-lambda/bin/chromium.br'
        to: './bin'   
      - from: 'node_modules/chrome-aws-lambda/bin/swiftshader.tar.br'
        to: './bin'   
provider: 
  name: aws
  runtime: nodejs12.x
  region: eu-central-1
  stage: test
  apiGateway: 
    shouldStartNameWithService: true
    binaryMediaTypes:
      - '*/*'
  tracing:
    apiGateway: true
    lambda: true
functions:
  downloadPdf:
    handler: lambdas/download-pdf.main
    events:
      - http:
          path: download-pdf
          method: get
          cors: true
    timeout: 180

4. Risultato

5. Conclusioni

La soluzione proposta evidenzia la semplicità e l'immediatezza per la generazione di un pdf con AWS lambda.

In alternativa a questo approccio, si può utilizzare il plugin PDFkit, ma la considero una strada più complessa e dispendiosa.

Il codice della soluzione proposta è visualizzabile in questo repository Github.

Se vi è un piaciuto il mio articolo, vi invito a condividerlo.

Il vostro supporto e feedback significa molto per noi.