Créez des PDF dynamiques en utilisant React et Node.js

Le matĂ©riel, dont nous publions la traduction aujourd'hui, est consacrĂ© Ă  la crĂ©ation de fichiers PDF dynamiques utilisant le code HTML comme modĂšle. À savoir, nous parlerons de la façon de crĂ©er une simple facture pour le paiement de certains biens ou services, dont les donnĂ©es dynamiques incluses sont extraites de l'Ă©tat de l'application React. La base de l'application React a Ă©tĂ© créée Ă  l'aide de create-react-app, la partie serveur du projet est basĂ©e sur Node.js et le framework Express a Ă©tĂ© utilisĂ© dans son dĂ©veloppement.



L'auteur de ce document note qu'il a préparé une vidéo qui montre le développement du projet. Si vous décidez de regarder la vidéo et de lire l'article, il est recommandé de le faire. Commencez par parcourir l'article, puis activez la vidéo et recréez le systÚme que vous envisagez. AprÚs cela, lisez simplement l'article.

Création de projet


Créez un répertoire de projet et accédez-y:

mkdir pdfGenerator && cd pdfGenerator 

Créez une nouvelle application React:

 create-react-app client 

AprÚs avoir terminé la création de l'application, accédez au répertoire que vous venez de créer et installez les dépendances:

 cd client && npm i -S axios file-saver 

Créez un serveur Express. Pour ce faire, créez le dossier du server dans le répertoire du projet et accédez-y. Dans celui-ci, créez le fichier index.js et démarrez l'initialisation du projet:

 mkdir server && cd server && touch index.js && npm init 

Ici, pour former package.json , appuyez simplement sur Enter plusieurs fois. AprÚs cela, exécutez la commande suivante pour ajouter les dépendances nécessaires à la partie serveur du projet:

 npm i -S express body-parser cors html-pdf 

Maintenant, dans le fichier client/package.json , au-dessus de la section de description des dépendances, ajoutez ce qui suit:

 "proxy": "http://localhost:5000/" 

Cela vous aidera Ă  travailler avec le serveur local Ă  partir du code client.

Vous devez maintenant ouvrir deux fenĂȘtres du terminal.

Dans la premiĂšre fenĂȘtre, accĂ©dez au rĂ©pertoire client et exĂ©cutez la commande suivante:

 npm start 

Dans la deuxiĂšme fenĂȘtre, accĂ©dez au dossier du server et exĂ©cutez la commande suivante:

 nodemon index.js 

Configuration initiale du client


La partie client de notre projet sera trĂšs simple.

Pour commencer, dans le src/App.js partie client de l'application, nous importons les dépendances dans le code:

 import axios from 'axios'; import { saveAs } from 'file-saver'; 

AprÚs cela, en haut de la description du composant, initialisez l'état:

 state = {   name: 'Adrian',   receiptId: 0,   price1: 0,   price2: 0, } 

Supprimons le balisage JSX standard créé dans le modÚle d'application à l'aide de create-react-app et renvoyé par la méthode render() . Insérez-y les éléments suivants:

 <div className="App">   <input type="text" placeholder="Name" name="name" onChange {this.handleChange}/>   <input type="number" placeholder="Receipt ID" name="receiptId"  onChange={this.handleChange}/>   <input type="number" placeholder="Price 1" name="price1" onChange={this.handleChange}/>   <input type="number" placeholder="Price 2" name="price2" onChange={this.handleChange}/>   <button onClick={this.createAndDownloadPdf}>Download PDF</button></div> 

Créons la méthode handleChange , qui sera responsable de la mise à jour des données d'état de l'application liées aux champs d'entrée:

 handleChange = ({ target: { value, name }}) => this.setState({ [name]: value }) 

Nous pouvons maintenant passer à la tùche de création d'un fichier PDF. Cette partie, qui est résolue au moyen du client, consiste à créer une demande POST au serveur. La demande envoie l'état de l'application:

 createAndDownloadPdf = () => {   axios.post('/create-pdf', this.state) } 

Avant de continuer à travailler sur le cÎté client du projet, nous devons configurer les itinéraires sur le serveur. Cela permettra au serveur de recevoir des données du client, de générer un fichier PDF et de retransférer ce fichier au client.

Configuration initiale du serveur


La partie serveur du projet ne comprendra que deux routes. Un est nécessaire pour créer des PDF. La seconde sert à envoyer des fichiers au client.

Tout d'abord, importez les dépendances dans le fichier index.js :

 const express = require('express'); const bodyParser = require('body-parser'); const pdf = require('html-pdf'); const cors = require('cors'); 

Nous initialisons l'application Express et configurons le port:

 const app = express(); const port = process.env.PORT || 5000; 

Configurez l'analyseur de requĂȘtes. Ce dont nous avons besoin sera disponible en tant que req.body de req.body . Nous allons Ă©galement configurer CORS afin que notre erreur de Cross-Origin Request Blocked n’interfĂšre pas avec notre travail:

 app.use(cors()); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); 

AprÚs cela, démarrez le serveur:

 app.listen(port, () => console.log(`Listening on port ${port}`)); 

Nous pouvons maintenant nous attaquer au code responsable de la création des PDF.

Créer un modÚle HTML pour les PDF


Nous avons besoin d'un modĂšle HTML Ă  utiliser lors de la crĂ©ation de fichiers PDF. En crĂ©ant un tel modĂšle, des possibilitĂ©s infinies s'ouvrent devant nous. Tout ce qui peut ĂȘtre créé en utilisant du HTML pur et du CSS peut ĂȘtre reprĂ©sentĂ© sous forme de fichier PDF. CrĂ©ez le rĂ©pertoire des documents dans le dossier du server , accĂ©dez-y et crĂ©ez-y le fichier index.js :

 mkdir documents && cd documents && touch index.js 

À partir de ce fichier, nous exportons une fonction flĂšche qui renverra tout le code HTML nĂ©cessaire. Lorsque vous appelez cette fonction, vous pouvez utiliser les paramĂštres, que nous dĂ©crirons Ă©galement ici.

 module.exports = ({ name, price1, price2, receiptId }) => { ... } 

Ici, je vais vous donner un exemple de modÚle HTML, et vous le copiez simplement dans votre projet. Mais vous pouvez bien sûr créer votre propre modÚle.

index.js code index.js du dossier server/documents au formulaire suivant:

 module.exports = ({ name, price1, price2, receiptId }) => {    const today = new Date(); return `    <!doctype html>    <html>       <head>          <meta charset="utf-8">          <title>PDF Result Template</title>          <style>             .invoice-box {             max-width: 800px;             margin: auto;             padding: 30px;             border: 1px solid #eee;             box-shadow: 0 0 10px rgba(0, 0, 0, .15);             font-size: 16px;             line-height: 24px;             font-family: 'Helvetica Neue', 'Helvetica',             color: #555;             }             .margin-top {             margin-top: 50px;             }             .justify-center {             text-align: center;             }             .invoice-box table {             width: 100%;             line-height: inherit;             text-align: left;             }             .invoice-box table td {             padding: 5px;             vertical-align: top;             }             .invoice-box table tr td:nth-child(2) {             text-align: right;             }             .invoice-box table tr.top table td {             padding-bottom: 20px;             }             .invoice-box table tr.top table td.title {             font-size: 45px;             line-height: 45px;             color: #333;             }             .invoice-box table tr.information table td {             padding-bottom: 40px;             }             .invoice-box table tr.heading td {             background: #eee;             border-bottom: 1px solid #ddd;             font-weight: bold;             }             .invoice-box table tr.details td {             padding-bottom: 20px;             }             .invoice-box table tr.item td {             border-bottom: 1px solid #eee;             }             .invoice-box table tr.item.last td {             border-bottom: none;             }             .invoice-box table tr.total td:nth-child(2) {             border-top: 2px solid #eee;             font-weight: bold;             }             @media only screen and (max-width: 600px) {             .invoice-box table tr.top table td {             width: 100%;             display: block;             text-align: center;             }             .invoice-box table tr.information table td {             width: 100%;             display: block;             text-align: center;             }             }          </style>       </head>       <body>          <div class="invoice-box">             <table cellpadding="0" cellspacing="0">                <tr class="top">                   <td colspan="2">                      <table>                         <tr>                            <td class="title"><img src="https://i2.wp.com/cleverlogos.co/wp-content/uploads/2018/05/reciepthound_1.jpg?fit=800%2C600&ssl=1"                               style="width:100%; max-width:156px;"></td>                            <td>                               Datum: ${`${today.getDate()}. ${today.getMonth() + 1}. ${today.getFullYear()}.`}                            </td>                         </tr>                      </table>                   </td>                </tr>                <tr class="information">                   <td colspan="2">                      <table>                         <tr>                            <td>                               Customer name: ${name}                            </td>                            <td>                               Receipt number: ${receiptId}                            </td>                         </tr>                      </table>                   </td>                </tr>                <tr class="heading">                   <td>Bought items:</td>                   <td>Price</td>                </tr>                <tr class="item">                   <td>First item:</td>                   <td>${price1}$</td>                </tr>                <tr class="item">                   <td>Second item:</td>                   <td>${price2}$</td>                </tr>             </table>             <br />             <h1 class="justify-center">Total price: ${parseInt(price1) + parseInt(price2)}$</h1>          </div>       </body>    </html>    `; }; 

server/index.js incluons ce fichier dans le fichier server/index.js :

 const pdfTemplate = require('./documents'); 

Créer des PDF


Rappelons que sur le serveur, nous allons créer deux routes. La route POST sera chargée de recevoir les données du client et de créer un fichier PDF. La route GET sera utilisée pour envoyer le fichier fini au client.

▍ itinĂ©raire create-pdf


Dans la route POST create-pdf , nous utiliserons la commande pdf.create() , faisant référence à l'objet importé du module html-pdf .

En tant que premier paramÚtre de la méthode pdf.create() , un modÚle HTML est utilisé, ainsi que les données reçues du client.

Pour renvoyer pdf.create() , nous appelons la méthode toFile() , en lui passant le nom que nous voulons attribuer au document PDF, ainsi que la fonction de rappel de flÚche. Cette fonction, en cas d'erreur, exécutera la commande res.send(Promise.reject()) . res.send(Promise.resolve()) tout se passe bien, elle exécutera la commande res.send(Promise.resolve()) .

 app.post('/create-pdf', (req, res) => {    pdf.create(pdfTemplate(req.body), {}).toFile('result.pdf', (err) => {        if(err) {            res.send(Promise.reject());        }        res.send(Promise.resolve());    }); }); 

RouteFetch-pdf Route


Dans le mĂȘme temps, nous crĂ©erons un itinĂ©raire qui sera utilisĂ© aprĂšs que, Ă  la demande du client, un fichier PDF ait Ă©tĂ© créé avec succĂšs. Ici, nous prenons simplement le document fini et l'envoyons au client en utilisant res.sendFile() :

 app.get('/fetch-pdf', (req, res) => {    res.sendFile(`${__dirname}/result.pdf`) }) 

Fonction client createAndDownloadPdf


Nous pouvons maintenant revenir au code client et continuer Ă  travailler sur la fonction createAndDownloadPdf . Ici, nous faisons une requĂȘte POST au serveur en utilisant le module axios . Maintenant, cette fonction ressemble Ă  ceci:

 createAndDownloadPdf = () => {   axios.post('/create-pdf', this.state) } 

Si aprĂšs avoir exĂ©cutĂ© une requĂȘte POST sur le serveur, un document PDF a Ă©tĂ© créé, nous devons exĂ©cuter une requĂȘte GET, en rĂ©ponse Ă  laquelle le serveur enverra le document fini au client.

Pour implĂ©menter ce schĂ©ma de comportement, nous, aprĂšs avoir appelĂ© axios.post() , appelons. then() . Cela nous permet de le faire en rĂ©ponse Ă  la demande POST du client, nous renvoyons une promesse du serveur qui peut ĂȘtre rĂ©solue ou rejetĂ©e avec succĂšs.

Nous complétons la fonction avec le code suivant:

 .then(() => axios.get('/fetch-pdf', { responseType: 'blob' })) .then(( res) => {}) 

Ici, vous pouvez faire attention au fait que blob utilisé comme responseType . Avant d'aller plus loin, parlons de ce que c'est.

Objets blob


Blob est un objet immuable reprĂ©sentant des donnĂ©es brutes. Ces objets sont souvent utilisĂ©s pour travailler avec des donnĂ©es qui peuvent ne pas ĂȘtre au format JavaScript natif. Ces objets sont des sĂ©quences d'octets qui stockent, par exemple, des donnĂ©es de fichier. Il peut sembler que l'objet Blob stocke un lien vers un fichier, mais en fait ce n'est pas le cas. Ces objets stockent des donnĂ©es avec lesquelles vous pouvez travailler. Par exemple, ils peuvent ĂȘtre enregistrĂ©s dans des fichiers.

AchĂšvement du projet


Maintenant que nous savons ce que sont les objets Blob , nous pouvons utiliser un autre appel .then() pour y res.data nouvel objet Blob , basé sur res.data. Lors de la création de cet objet, nous transmettrons un paramÚtre à son constructeur, indiquant que les données que l'objet va stocker sont de type application/pdf . AprÚs cela, nous pouvons utiliser la méthode saveAs() , qui a été importée du module file-saver , et enregistrer les données dans un fichier. Par conséquent, le code de la méthode createAndDowndloadPdf ressemblera à celui illustré ci-dessous:

   createAndDownloadPdf = () => {    axios.post('/create-pdf', this.state)      .then(() => axios.get('fetch-pdf', { responseType: 'blob' }))      .then((res) => {        const pdfBlob = new Blob([res.data], { type: 'application/pdf' });        saveAs(pdfBlob, 'newPdf.pdf');      })  } 

Voici Ă  quoi ressemble l'interface du navigateur.


Application dans le navigateur

AprÚs avoir rempli les champs et cliqué sur le bouton Download PDF , les données sont transférées vers le serveur et un document PDF est téléchargé depuis celui-ci.


Téléchargez le PDF

Et voici le document PDF lui-mĂȘme.


PDF généré par logiciel

Résumé


Nous avons examiné un mécanisme qui vous permet de créer par programme des fichiers PDF. Voici un référentiel GitHub avec le code du projet. Nous espérons que les idées que vous avez rencontrées dans ce document trouveront une application dans votre développement.

Chers lecteurs! Comment résoudriez-vous le problÚme de la création de fichiers PDF par programme à l'aide de Node.js?

Source: https://habr.com/ru/post/fr460018/


All Articles