Jak wygenerować tabelę HTML i plik PDF za pomocą Node i Google Puppeteer

Conoce la tierra.

Zrozumienie NodeJS wewnętrznie może być trochę zniechęcające (wiem, że kiedyś to było dla mnie). Węzeł to bardzo potężny język, który potrafi wiele rzeczy.

Dzisiaj chciałem odkryć możliwości wbudowanego narzędzia Node o nazwie fs (system plików)

Zgodnie z dokumentacją fs:

fsModuł zawiera API dla interakcji z systemem plików w sposób ściśle modelowanego około standardowych funkcji POSIX.

To po prostu fantazyjny sposób powiedzenia, że ​​system plików jest sposobem w Node na interakcję z plikami zarówno w przypadku operacji odczytu, jak i zapisu.

Teraz system plików to ogromne narzędzie w NodeJS, które ma wiele wymyślnych funkcji. W tym artykule omówię jednak tylko 3:

  • Pobieranie informacji o pliku: fs.statSync
  • Usuwanie pliku: fs.unlinkSync
  • Zapisywanie danych do pliku: fs.writeFileSync

Kolejną rzeczą, którą omówimy w tym artykule, jest Google Puppeteer, które jest naprawdę fajnym, zręcznym narzędziem stworzonym przez niesamowitych ludzi z Google.

Więc kim jest lalkarz? Jak na dokumentację, mówią:

Puppeteer to biblioteka Node, która zapewnia wysokopoziomowy interfejs API do kontroli bezgłowego Chrome lub Chromium za pośrednictwem protokołu DevTools. Można go również skonfigurować do korzystania z pełnego (bezgłowego) Chrome lub Chromium.

Jest to więc w zasadzie narzędzie, które pozwala robić wszystkie fajne rzeczy związane z przeglądarką na serwerze. Na przykład pobieranie zrzutów ekranu witryny, indeksowanie witryn internetowych i generowanie wstępnie renderowanej zawartości dla aplikacji jednostronicowych. Możesz nawet przesyłać formularze za pośrednictwem serwera NodeJS.

Ponownie lalkarz to ogromne narzędzie, więc omówimy tylko małą, ale bardzo fajną funkcję lalkarza. Przyjrzymy się, jak wygenerować ładny plik PDF na podstawie naszego wygenerowanego pliku tabeli HTML. W trakcie tego procesu nauczymy się o puppeteer.launch () i trochę zrozumiemy o page () i pdf ().

Aby ponownie przedstawić krótki przegląd, omówimy:

  • Generowanie danych pośrednich (dla faktur) za pomocą narzędzia online.
  • Stworzenie tabeli HTML z odrobiną stylizacji z wygenerowanymi danymi za pomocą automatycznego skryptu węzłów.
  • Dowiedz się, jak sprawdzić, czy plik istnieje, czy nie przy użyciu fs.statSync
  • Dowiedz się, jak usunąć plik za pomocą fs.unlinkSync
  • Nauka pisania pliku za pomocą fs.writeFileSync
  • Utworzenie pliku PDF zawierającego ten plik HTML wygenerowany za pomocą lalkarza Google
  • Robisz z nich skrypty npm do późniejszego wykorzystania? ?
Również zanim zaczniemy tutaj, jest cały kod źródłowy samouczka, do którego wszyscy powinni się przyczepić. Nie musisz nic pisać, ale powinieneś pisać kod wraz z tym samouczkiem. To okaże się bardziej przydatne i zrozumiesz więcej. KOD ŹRÓDŁOWY TUTORIALU

Zanim zaczniemy, upewnij się, że masz co najmniej następujące elementy zainstalowane na swoim komputerze

  • Wersja węzła 8.11.2
  • Node Package Manager (NPM) w wersji 6.9.0

Nie musisz, ale możesz też obejrzeć film wprowadzający (mój pierwszy w historii), który mówi o podstawach czytania, pisania i usuwania pliku w NodeJS. Pomoże Ci to zrozumieć ten samouczek. (Proszę o informację zwrotną). ?

Zacznijmy

Krok 1:

W swoim terminalu wpisz:

npm init -y

Spowoduje to zainicjowanie dla Ciebie pustego projektu.

Krok 2:

Po drugie, w tym samym folderze utwórz nowy plik o nazwie data.jsoni umieść w nim fałszywe dane. Możesz użyć następującego przykładu JSON.

Możesz pobrać fałszywe dane pośredniczące JSON tutaj . Do wygenerowania tych danych użyłem niesamowitego narzędzia o nazwie //mockaroo.com/ Jest to narzędzie do generowania danych online.

Dane JSON, z którymi mam zamiar, mają taką strukturę:

[ {}, {}, { "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 }, {}, {} ]
Możesz pobrać pełną tablicę JSON do tego samouczka stąd .

Krok 3:

Następnie utwórz nowy plik o nazwie buildPaths.js

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

Więc path.resolveweźmie ścieżkę względną i zwróci nam bezwzględną ścieżkę do tego konkretnego katalogu.

Więc path.resolve('./build.html');wolę czegoś przykład powrotnej tak:

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

Krok 4:

W tym samym folderze utwórz plik o nazwie createTable.jsi dodaj następujący kod:

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 IdInvoice NamePriceInvoice CreatedDue DateVendor AddressVendor 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).

Należy tu zauważyć, że możemy nie potrzebować kroku 4, w którym sprawdzamy, czy plik istnieje, a jeśli tak, usuwamy go. Dzieje się tak, ponieważ writeFileSync robi to za nas. Właśnie dodałem to w kodzie w celach edukacyjnych.

Krok 7:

W terminalu przejdź do ścieżki folderu, w której masz createTable.jsi wpisz

$ npm run ./createTable.js

Jak tylko uruchomisz ten skrypt, utworzy on nowy plik w tym samym folderze o nazwie build.htmlMożesz otworzyć ten plik w przeglądarce i będzie on wyglądał mniej więcej tak.

Fajnie, prawda? Na razie w porządku. ?

Możesz również dodać do npm scriptpliku package.json w następujący sposób:

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

W ten sposób zamiast pisać npm run ./createTable.js, możesz po prostu wpisać npm run build:table.

Dalej: generowanie pliku PDF z wygenerowanego HTMLpliku.

Krok 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.

Najpierw w metodzie init () wywołujemy metodę printPdf () , która ponownie jest metodą asynchroniczną, więc musimy poczekać na jej odpowiedź. Metoda printPdf () zwraca nam instancję PDF, którą następnie zapisujemy w pliku w linii 33.

Więc co robi ta printPdf()metoda? Zagłębmy się w to.

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;

Najpierw uruchamiamy bezgłową instancję przeglądarki za pomocą lalkarza, wykonując następujące czynności:

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

którego następnie używamy do otwierania strony internetowej:

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

Gdy otworzymy pustą stronę, możemy przejść do strony. Ponieważ nasza strona internetowa jest lokalnie w naszym systemie, po prostu

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

Tutaj waitUntil: 'networkidle0;jest ważna, ponieważ mówi lalkarza czekać na 500 / ms, dopóki nie ma więcej połączeń sieciowych.

Uwaga: Dlatego użyliśmy path.resolve (), aby uzyskać ścieżki bezwzględne, ponieważ aby otworzyć stronę internetową za pomocą lalkarza, potrzebujemy ścieżki bezwzględnej.

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:

I to wszystko, skończyliśmy.

Nauczyłeś się następujących rzeczy:

  • Jak sprawdzić, czy plik istnieje / informacje o pliku tet (w węźle)
  • Jak usunąć plik w Node
  • Jak pisać do pliku
  • Jak używać Google Puppeteer do generowania pliku PDF

Przyjemnej nauki, chciałbym poznać Twoje przemyślenia na temat tego artykułu. Możesz skontaktować się ze mną na Twitterzetakże.