Как да генерирам HTML таблица и PDF с Node и Google Puppeteer

Conoce la tierra.

Разбирането на NodeJS отвътре може да бъде малко обезсърчително (знам, че веднъж беше за мен). Node е много мощен език и може да направи много неща.

Днес исках да разкрия силата на вградения инструмент на Node, наречен fs (файлова система)

Съгласно fs документите:

В fsмодул предоставя API за взаимодействие с файловата система по начин, внимателно моделирани около стандартните функции POSIX.

Което е просто изискан начин да се каже, че файловата система е начин в Node да взаимодейства с файлове както за операции за четене, така и за запис.

Сега файловата система е огромна програма в NodeJS, която има много изискани функции. В тази статия обаче ще обсъдя само 3:

  • Получаване на информация за файла: fs.statSync
  • Изтриване на файл: fs.unlinkSync
  • Записване на данни във файл: fs.writeFileSync

Друго нещо, което ще разгледаме в тази статия, е Google Puppeteer, който е този наистина страхотен, хлъзгав инструмент, създаден от някои страхотни хора в Google.

И така, какво е кукловод? Ами според документите, те казват:

Puppeteer е Node библиотека, която осигурява API на високо ниво за управление на Chromeless или Chromium без глава през протокола DevTools. Той може също да бъде конфигуриран да използва пълен (без глави) Chrome или Chromium.

Така че това е основно инструмент, който ви позволява да правите всички страхотни неща, свързани с браузъра на сървъра. Като получаване на екранни снимки на уебсайт, обхождане на уебсайтове и генериране на предварително визуализирано съдържание за приложения на една страница. Можете дори да правите изпращания на формуляри чрез вашия сървър NodeJS.

Отново кукловодът е огромен инструмент, така че ще разгледаме само една малка, но много готина характеристика на кукловода. Ще разгледаме как да генерираме хубав PDF файл въз основа на генерирания ни файл с HTML таблица. В процеса ще научим за puppeteer.launch () и ще разберем малко за page () & pdf ().

И така, за да дадем отново кратък преглед, нещата, които ще разгледаме:

  • Генериране на данни за мъниче (за фактури) с помощта на онлайн инструмент.
  • Създаване на HTML таблица с малко стилизиране с генерирани данни в нея, използвайки автоматизиран скрипт на възел.
  • Научаване за проверка дали файл съществува или не използва fs.statSync
  • Научаване за изтриване на файл с помощта на fs.unlinkSync
  • Научаване за писане на файл с помощта на fs.writeFileSync
  • Създаване на PDF файл от този HTML файл, генериран с помощта на кукловод на Google
  • Превръщането им в npm скриптове, които да бъдат използвани по-късно? ?
Също така, преди да започнем тук, е целият изходен код на урока, който всички да следват. Не е нужно да пишете нищо, но трябва да напишете код заедно с този урок. Това ще се окаже по-полезно и ще разберете повече. ИЗТОЧНИК КОДЕКС НА УЧАБНИКА

Преди да започнем, уверете се, че сте инсталирали поне следното на вашето устройство

  • Версия на възел 8.11.2
  • Node Package Manager (NPM) версия 6.9.0

Не е нужно, но можете да гледате и уводно видео (първото ми направено), което разказва за основите при четене, писане и изтриване на файл в NodeJS. Това ще ви помогне да разберете този урок. (Моля, дайте ми обратна връзка). ?

Да започваме

Етап 1:

Въведете следното в терминала си:

npm init -y

Това ще инициализира празен проект за вас.

Стъпка 2:

Второ, в същата папка създайте нов файл, наречен data.jsonи в него има някои подигравани данни. Можете да използвате следната проба JSON.

Можете да получите най-подиграваха JSON е все още мъниче от тук . За генерирането на тези данни използвах страхотен инструмент, наречен //mockaroo.com/ Това е инструмент за онлайн генериране на данни.

Данните JSON, с които отивам, имат структура като тази:

[ {}, {}, { "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 }, {}, {} ]
Можете да изтеглите пълния JSON масив за този урок от тук .

Стъпка 3:

След това създайте нов файл, наречен buildPaths.js

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

Така че path.resolveще поеме в относителен път и ще ни върне абсолютния път на тази конкретна директория.

Така path.resolve('./build.html');например ще върне нещо подобно:

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

Стъпка 4:

В същата папка създайте извикан файл createTable.jsи добавете следния код:

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

Тук трябва да отбележим едно нещо, че може да не се нуждаем от Стъпка 4, където проверяваме дали файлът съществува и ако го има, след това го изтрийте. Това е така, защото writeFileSync прави това за нас. Току-що добавих това в кода с учебна цел.

Стъпка 7:

Във вашия терминал отидете в пътя на папката, където имате createTable.jsи тип

$ npm run ./createTable.js

Веднага след като стартирате този скрипт, той ще създаде нов файл в същата папка, наречен build.htmlМожете да отворите този файл в браузъра си и той ще изглежда нещо подобно.

Готино нали? Дотук добре. ?

Също така можете да добавите npm scriptвъв вашия package.json по следния начин:

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

По този начин, вместо да пишете npm run ./createTable.js, можете просто да въведете npm run build:table.

Следва: генериране на PDF от генерирания HTMLфайл.

Стъпка 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.

Първо в метода init () извикваме метода printPdf () , който отново е асинхронен метод, така че трябва да изчакаме отговора му. Методът printPdf () ни връща PDF екземпляр, който след това записваме във файл на ред 33.

И така, какво прави printPdf()методът? Нека да копаем дълбоко в него.

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;

Първо стартираме екземпляр на браузър без глава с помощта на кукловод, като правим следното:

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

които след това използваме за отваряне на уеб страница:

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

След като отворим празна страница, можем да отидем до нея. Тъй като нашата уеб страница е локално в нашата система, ние просто

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

Тук waitUntil: 'networkidle0;е важно, тъй като казва на кукловода да изчака 500 / ms, докато няма повече мрежови връзки.

Забележка: Ето защо използвахме path.resolve (), за да получим абсолютни пътеки, защото за да отворим уеб страницата с кукловод, ни е необходим абсолютен път.

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:

И това е, приключихме.

Научихте следното:

  • Как да проверите дали съществува файл / информация за тет файл (в Node)
  • Как да изтрия файл в Node
  • Как да пиша във файл
  • Как да използвам Google Puppeteer за генериране на PDF файл

Приятно учене, бих се радвал да чуя вашите мисли за тази статия. Можете да се свържете с мен в Twitterкакто добре.