Cómo generar una tabla HTML y un PDF con Node y Google Puppeteer

Conoce la tierra.

Comprender NodeJS internamente puede ser un poco abrumador (sé que lo fue para mí una vez). Node es un lenguaje muy poderoso y puede hacer muchas cosas.

Hoy quería descubrir el poder de la herramienta de utilidad incorporada de Node llamada fs (sistema de archivos)

Según los documentos de fs:

El fsmódulo proporciona una API para interactuar con el sistema de archivos de una manera estrechamente modelada en torno a las funciones POSIX estándar.

Lo cual es solo una forma elegante de decir que el sistema de archivos es una forma en Node de interactuar con archivos para operaciones de lectura y escritura.

Ahora el sistema de archivos es una utilidad enorme en NodeJS que tiene muchas características sofisticadas. En este artículo, sin embargo, solo discutiré 3:

  • Obtener información de archivo: fs.statSync
  • Eliminar un archivo: fs.unlinkSync
  • Escribir datos en un archivo: fs.writeFileSync

Otra cosa que cubriremos en este artículo es Google Puppeteer, que es esta herramienta realmente genial y hábil creada por algunas personas increíbles en Google.

Entonces, ¿qué es titiritero? Bueno, según los documentos, dicen:

Puppeteer es una biblioteca de nodo que proporciona una API de alto nivel para controlar Chrome o Chromium sin cabeza a través del protocolo DevTools. También se puede configurar para usar Chrome o Chromium completo (no sin cabeza).

Entonces, es básicamente una herramienta que te permite hacer todas las cosas interesantes relacionadas con el navegador en el servidor. Como obtener capturas de pantalla de un sitio web, rastrear sitios web y generar contenido de procesamiento previo para aplicaciones de una sola página. Incluso puede enviar formularios a través de su servidor NodeJS.

Nuevamente, el titiritero es una herramienta enorme, por lo que cubriremos solo una característica pequeña pero muy interesante del titiritero. Veremos cómo generar un buen archivo PDF basado en nuestro archivo de tabla HTML generado. En el proceso, aprenderemos sobre puppeteer.launch () y entenderemos un poco sobre page () y pdf ().

Entonces, para dar nuevamente una breve descripción, cubriremos:

  • Generación de datos de talón (para facturas) utilizando una herramienta en línea.
  • Crear una tabla HTML con un poco de estilo con datos generados en ella, usando un script de nodo automatizado.
  • Aprender a verificar si un archivo existe o no usar fs.statSync
  • Aprender a eliminar un archivo mediante fs.unlinkSync
  • Aprender a escribir un archivo usando fs.writeFileSync
  • Creación de un archivo PDF de ese archivo HTML generado con el titiritero de Google
  • ¿Convertirlos en scripts npm para usarlos más tarde? ?
También antes de comenzar aquí está el código fuente completo del tutorial, para que todos lo sigan. No tiene que escribir nada, pero debe escribir código junto con este tutorial. Eso resultará más útil y comprenderá más. CÓDIGO FUENTE DEL TUTORIAL

Antes de comenzar, asegúrese de tener al menos lo siguiente instalado en su máquina

  • Nodo versión 8.11.2
  • Node Package Manager (NPM) versión 6.9.0

No es necesario, pero también puede ver un video introductorio (el primero que hice) que habla sobre los conceptos básicos para leer, escribir y eliminar un archivo en NodeJS. Esto le ayudará a comprender este tutorial. (Por favor, dame tu opinión). ?

Empecemos

Paso 1:

En su terminal, escriba lo siguiente:

npm init -y

Esto inicializará un proyecto vacío para usted.

Paso 2:

En segundo lugar, en la misma carpeta, cree un nuevo archivo llamado data.jsony tenga algunos datos simulados en él. Puede utilizar el siguiente ejemplo de JSON.

Puede obtener los datos de código auxiliar JSON simulados desde aquí . Para generar estos datos, he utilizado una herramienta increíble llamada //mockaroo.com/ Es una herramienta generadora de datos en línea.

Los datos JSON con los que voy tienen una estructura como esta:

[ {}, {}, { "invoiceId": 1, "createdDate": "3/27/2018", "dueDate": "5/24/2019", "address": "28058 Hazelcrest Center", "companyName": "Eayo", "invoiceName": "Carbonated Water - Peach", "price": 376 }, { "invoiceId": 2, "createdDate": "6/14/2018", "dueDate": "11/14/2018", "address": "6205 Shopko Court", "companyName": "Ozu", "invoiceName": "Pasta - Fusili Tri - Coloured", "price": 285 }, {}, {} ]
Puede descargar la matriz JSON completa para este tutorial desde aquí .

Paso 3:

A continuación, cree un nuevo archivo llamado buildPaths.js

const path = require('path'); const buildPaths = { buildPathHtml: path.resolve('./build.html'), buildPathPdf: path.resolve('./build.pdf') }; module.exports = buildPaths;

Entonces path.resolvetomará una ruta relativa y nos devolverá la ruta absoluta de ese directorio en particular.

Entonces path.resolve('./build.html');, por ejemplo, devolverá algo como esto:

$ C:\\Users\\Adeel\\Desktop\\articles\\tutorial\\build.html

Etapa 4:

En la misma carpeta crea un archivo llamado createTable.jsy agrega el siguiente código:

const fs = require('fs'); // JSON data const data = require('./data.json'); // Build paths const { buildPathHtml } = require('./buildPaths'); /** * Take an object which has the following model * @param {Object} item * @model * { * "invoiceId": `Number`, * "createdDate": `String`, * "dueDate": `String`, * "address": `String`, * "companyName": `String`, * "invoiceName": `String`, * "price": `Number`, * } * * @returns {String} */ const createRow = (item) => ` ${item.invoiceId}${item.invoiceName}${item.price}${item.createdDate}${item.dueDate}${item.address}${item.companyName} `; /** * @description Generates an `html` table with all the table rows * @param {String} rows * @returns {String} */ const createTable = (rows) => ` 
     ${rows} 
     
Invoice Id Invoice Name Price Invoice Created Due Date Vendor Address Vendor Name
`; /** * @description Generate an `html` page with a populated table * @param {String} table * @returns {String} */ const createHtml = (table) => ` table { width: 100%; } tr { text-align: left; border: 1px solid black; } th, td { padding: 15px; } tr:nth-child(odd) { background: #CCC } tr:nth-child(even) { background: #FFF } .no-content { background-color: red; } ${table} `; /** * @description this method takes in a path as a string & returns true/false * as to if the specified file path exists in the system or not. * @param {String} filePath * @returns {Boolean} */ const doesFileExist = (filePath) => { try { fs.statSync(filePath); // get information of the specified file path. return true; } catch (error) { return false; } }; try { /* Check if the file for `html` build exists in system or not */ if (doesFileExist(buildPathHtml)) { console.log('Deleting old build file'); /* If the file exists delete the file from system */ fs.unlinkSync(buildPathHtml); } /* generate rows */ const rows = data.map(createRow).join(''); /* generate table */ const table = createTable(rows); /* generate html */ const html = createHtml(table); /* write the generated html to file */ fs.writeFileSync(buildPathHtml, html); console.log('Succesfully created an HTML table'); } catch (error) { console.log('Error generating table', error); }

I know that is a lot of code, but let’s divide it into chunks and start understanding it piece by piece.

Go to line 106 (github gist)

In our try/catch block we first check if the build file for HTML exists in the system or not. This is the path of the file where our NodeJS script will generate our HTML.

if (doesFileExist(buildPathHtml){} calls doesFileExist() method which simply returns true/false. For this we use

fs.statSync(filePath);

This method actually returns information about the file like the size of the file, when the file was created, and so on. However if we provide it an invalid file path, this method returns as a null error. Which we use here to our benefit and wrap the fs.statSync() method in a try/catch. If Node is successfully able to read the file in our try block, we return true — otherwise it throws an error which we get in our catch block and returns false.

If the file exists in the system we end up deleting the file using

fs.unlinkSync(filePath); // takes in a file path & deletes it

After deleting the file, we need to generate rows to put in the table.

Step 5:

So first we import data.json which we do at line 3 & then on line 115 we iterate each item using map(). You can read more about Array.prototype.map() here.

The map method takes a method createRow which takes in an object through each iteration and returns a string which has content like this:

"invoice idinvoice nameinvoice priceinvoice created dateinvoice due dateinvoice addressinvoice sender company name"
const row = data.map(createdRow).join('');

The join('') part is important here, because I want to concatenate all of my array into a string.

An almost similar principle is used for generating a table on line 117 & then the html table on line 119.

Step 6:

The important part is where we write to our file on line 121:

fs.writeFileSync(buildPathHtml, html); 

It takes in 2 parameters: one is the build path (string) and the html content (string) and generates a file (if not created; and if it is created, it overwrites the already existing file).

Una cosa a tener en cuenta aquí es posible que no necesitemos el Paso 4, donde verificamos si el archivo existe y, si existe, lo eliminamos. Esto se debe a que writeFileSync lo hace por nosotros. Acabo de agregar eso en el código con fines de aprendizaje.

Paso 7:

En su terminal, vaya a la ruta de la carpeta donde tiene el createTable.jsy escriba

$ npm run ./createTable.js

Tan pronto como ejecute este script, creará un nuevo archivo en la misma carpeta llamada build.htmlPuede abrir ese archivo en su navegador y se verá así.

¿Guay, verdad? Hasta aquí todo bien. ?

También puede agregar un npm scripten su package.json de esta manera:

"scripts": { "build:table": "node ./createTable.js" },

De esta manera, en lugar de escribir npm run ./createTable.js, simplemente puede escribir npm run build:table.

A continuación: generar un PDF a partir del HTMLarchivo generado .

Paso 8:

First things first we need to install a fancy tool, so go in your terminal in your application folder and type in

npm install puppeteer

Step 9:

In the same folder where you have files createTable.js , buildPaths.js & data.json, create a new file called createPdf.js and add content to it like below:

 const fs = require('fs'); const puppeteer = require('puppeteer'); // Build paths const { buildPathHtml, buildPathPdf } = require('./buildPaths'); const printPdf = async () => { console.log('Starting: Generating PDF Process, Kindly wait ..'); /** Launch a headleass browser */ const browser = await puppeteer.launch(); /* 1- Ccreate a newPage() object. It is created in default browser context. */ const page = await browser.newPage(); /* 2- Will open our generated `.html` file in the new Page instance. */ await page.goto(buildPathHtml, { waitUntil: 'networkidle0' }); /* 3- Take a snapshot of the PDF */ const pdf = await page.pdf({ format: 'A4', margin: { top: '20px', right: '20px', bottom: '20px', left: '20px' } }); /* 4- Cleanup: close browser. */ await browser.close(); console.log('Ending: Generating PDF Process'); return pdf; }; const init = async () => { try { const pdf = await printPdf(); fs.writeFileSync(buildPathPdf, pdf); console.log('Succesfully created an PDF table'); } catch (error) { console.log('Error generating PDF', error); } }; init();

As we did with createTable.js script, let’s break this down into chunks and start understanding this script step by step.

Let’s start with line 40: here we call a method init() which calls the method on line 30. Onething to focus on is that our init() method is an async method. Read more on this async function.

Primero en el método init () llamamos al método printPdf () que es nuevamente un método asíncrono, por lo que tenemos que esperar su respuesta. El método printPdf () nos devuelve una instancia de PDF que luego escribimos en un archivo en la línea 33.

Entonces, ¿qué hace el printPdf()método? Profundicemos en ello.

const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(buildPathHtml, { waitUntil: 'networkidle0' }); const pdf = await page.pdf({ format: 'A4', margin: { top: '20px', right: '20px', bottom: '20px', left: '20px'} }); await browser.close(); return pdf;

Primero lanzamos una instancia de navegador sin cabeza usando puppeteer haciendo lo siguiente:

await puppeteer.launch(); // this returns us headless browser

que luego usamos para abrir una página web:

await browser.newPage(); // open a blank page in headless browser

Una vez que tengamos una página en blanco abierta, podemos navegar a una página. Dado que nuestra página web está localmente en nuestro sistema, simplemente

page.goto(buildPathHtml, { waitUntil: 'networkidle0' });

Aquí waitUntil: 'networkidle0;es importante, porque le dice al titiritero que espere 500 / ms hasta que no haya más conexiones de red.

Nota: Es por esto que usamos path.resolve () para obtener rutas absolutas, porque para abrir la página web con titiritero, necesitamos una ruta absoluta.

After we have a web page opened in the headless browser on the server, we save that page as a pdf:

await page.pdf({ });

As soon as we have a pdf version of the web page, we need to close the browser instance opened by puppeteer to save resources by doing this:

await browser.close();

& then we return the pdf saved, which we then write to the file.

Step 10:

In your terminal type

$ npm ./createPdf.js

Note: Before running the above script, ensure that you the build.html file generated by createTable.js script. This ensures we always have the build.html prior to running the createPdf.js script. In your package,json do the following.

"scripts": { "build:table": "node ./createTable.js", "prebuild:pdf": "npm run build:table", "build:pdf": "node ./createPdf.js" },

Now if you run $ npm run build:pdf it will execute the createTable.js script first and then createPdf.js script. You can read more on NPM scripts on their official docs.

When you run

$ npm run build:pdf

It will run and create a build.pdf which will look like this:

Y eso es todo, hemos terminado.

Ha aprendido lo siguiente:

  • Cómo comprobar si existe un archivo / información del archivo tet (en el nodo)
  • Cómo eliminar un archivo en el nodo
  • Cómo escribir en un archivo
  • Cómo utilizar Google Puppeteer para generar un archivo PDF

Feliz aprendizaje, me encantaría escuchar tu opinión sobre este artículo. Puedes contactarme en twittertambién.