Erstellen Sie dynamische PDFs mit React und Node.js.

Das Material, dessen Übersetzung wir heute veröffentlichen, ist der Erstellung dynamischer PDF-Dateien unter Verwendung von HTML-Code als Vorlage gewidmet. Wir werden nämlich darüber sprechen, wie eine einfache Rechnung für die Zahlung bestimmter Waren oder Dienstleistungen erstellt wird. Die darin enthaltenen dynamischen Daten stammen aus dem Status der React-Anwendung. Die Basis der React-Anwendung wurde mit create-react-app erstellt, der Serverteil des Projekts basiert auf Node.js und das Express-Framework wurde bei seiner Entwicklung verwendet.



Der Autor dieses Materials stellt fest, dass er ein Video vorbereitet hat, das die Entwicklung des Projekts demonstriert. Wenn Sie sich das Video ansehen und den Artikel lesen möchten, wird dies empfohlen. Blättern Sie zuerst durch den Artikel, schalten Sie dann das Video ein und erstellen Sie das System neu, das Sie dort in Betracht ziehen. Lesen Sie danach einfach den Artikel.

Projekterstellung


Erstellen Sie ein Projektverzeichnis und gehen Sie dorthin:

mkdir pdfGenerator && cd pdfGenerator 

Erstellen Sie eine neue React-Anwendung:

 create-react-app client 

Wechseln Sie nach Abschluss der Anwendungserstellung in das gerade erstellte Verzeichnis und installieren Sie die Abhängigkeiten:

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

Erstellen Sie einen Express-Server. Erstellen Sie dazu den Serverordner im Projektverzeichnis und wechseln Sie dorthin. Erstellen Sie darin die Datei index.js und starten Sie die Projektinitialisierung:

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

package.json zu bilden, drücken Enter einfach mehrmals die Enter . Führen Sie anschließend den folgenden Befehl aus, um dem Serverteil des Projekts die erforderlichen Abhängigkeiten hinzuzufügen:

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

client/package.json nun in der Datei client/package.json über dem Abschnitt zur Beschreibung der Abhängigkeit Folgendes hinzu:

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

Dies hilft bei der Arbeit mit dem lokalen Server über den Client-Code.

Jetzt müssen Sie zwei Fenster des Terminals öffnen.

Wechseln Sie im ersten Fenster in das client Verzeichnis und führen Sie den folgenden Befehl aus:

 npm start 

Wechseln Sie im zweiten Fenster zum Serverordner und führen Sie den folgenden Befehl aus:

 nodemon index.js 

Erstes Client-Setup


Der Client-Teil unseres Projekts wird sehr einfach aussehen.

Zunächst importieren wir in der src/App.js Client-Teils der Anwendung die Abhängigkeiten in den Code:

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

Initialisieren Sie anschließend oben in der Komponentenbeschreibung den Status:

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

Entfernen wir das Standard-JSX-Markup, das in der Anwendungsvorlage mithilfe der create-react-app und von der render() -Methode zurückgegeben wurde. Fügen Sie Folgendes ein:

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

Erstellen wir die handleChange Methode, die für die Aktualisierung der Anwendungsstatusdaten in Bezug auf die Eingabefelder verantwortlich ist:

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

Jetzt können wir mit der Erstellung einer PDF-Datei fortfahren. Der Teil davon, der mittels des Clients gelöst wird, besteht darin, eine POST-Anfrage an den Server zu erstellen. Die Anforderung sendet den Anwendungsstatus:

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

Bevor wir auf der Clientseite des Projekts weiterarbeiten können, müssen wir die Routen auf dem Server konfigurieren. Auf diese Weise kann der Server Daten vom Client empfangen, eine PDF-Datei generieren und diese Datei zurück an den Client übertragen.

Erstes Server-Setup


Der Serverteil des Projekts enthält nur zwei Routen. Eine wird benötigt, um PDFs zu erstellen. Die zweite dient zum Senden von Dateien an den Client.

Importieren Sie zunächst die Abhängigkeiten in die Datei index.js :

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

Wir initialisieren die Express-Anwendung und konfigurieren den Port:

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

Richten Sie den Abfrageparser ein. Was wir brauchen, wird als req.body verfügbar req.body . Wir werden CORS auch so konfigurieren, dass unser Fehler " Cross-Origin Request Blocked unsere Arbeit nicht beeinträchtigt:

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

Starten Sie danach den Server:

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

Jetzt können wir den Code angehen, der für die Erstellung der PDFs verantwortlich ist.

Erstellen Sie eine HTML-Vorlage für PDFs


Für die Erstellung von PDF-Dateien benötigen wir eine HTML-Vorlage. Bei der Erstellung einer solchen Vorlage eröffnen sich uns endlose Möglichkeiten. Alles, was mit reinem HTML und CSS erstellt werden kann, kann als PDF-Datei dargestellt werden. Erstellen Sie das documents im index.js , gehen Sie dorthin und erstellen Sie die Datei index.js darin:

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

Aus dieser Datei exportieren wir eine Pfeilfunktion, die den gesamten erforderlichen HTML-Code zurückgibt. Wenn Sie diese Funktion aufrufen, können Sie die Parameter verwenden, die wir auch hier beschreiben werden.

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

Hier gebe ich Ihnen ein Beispiel für eine HTML-Vorlage, und Sie kopieren sie einfach in Ihr Projekt. Aber Sie können natürlich Ihre eigene Vorlage erstellen.

Lassen Sie uns index.js Code index.js aus dem Ordner server/documents in das folgende Formular bringen:

 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 diese Datei in die Datei server/index.js :

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

PDFs erstellen


Denken Sie daran, dass wir auf dem Server zwei Routen erstellen werden. Die POST-Route ist für den Empfang von Daten vom Client und die Erstellung einer PDF-Datei verantwortlich. Die GET-Route wird verwendet, um die fertige Datei an den Client zu senden.

▍ PDF-Route erstellen


In der POST-Route create-pdf wir den Befehl pdf.create() , der auf das aus dem html-pdf Modul importierte Objekt verweist.

Als erster Parameter der Methode pdf.create() werden eine HTML-Vorlage sowie vom Client empfangene Daten verwendet.

Um pdf.create() , rufen wir die Methode toFile() und übergeben ihr den Namen, den wir dem PDF-Dokument zuweisen möchten, sowie die Pfeilrückruffunktion. Diese Funktion führt im Fehlerfall den res.send(Promise.reject()) . Für den Fall, dass alles gut gegangen ist, führt sie den Befehl 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());    }); }); 

▍Fetch-pdf-Route


Gleichzeitig erstellen wir eine Route, die verwendet wird, nachdem auf Wunsch des Kunden eine PDF-Datei erfolgreich erstellt wurde. Hier nehmen wir einfach das fertige Dokument und senden es mit res.sendFile() an den Client:

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

Client-Funktion createAndDownloadPdf


Jetzt können wir zum Client-Code zurückkehren und weiter an der Funktion createAndDownloadPdf . Hier stellen wir mit dem axios Modul eine POST-Anfrage an den Server. Diese Funktion sieht nun folgendermaßen aus:

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

Wenn nach dem Ausführen einer POST-Anforderung an den Server ein PDF-Dokument erstellt wurde, müssen wir eine GET-Anforderung ausführen, auf die der Server das fertige Dokument an den Client sendet.

Um dieses Verhaltensschema zu implementieren, rufen wir nach dem Aufruf von axios.post() . then() . Auf diese Weise können wir als Antwort auf die POST-Anfrage eines Clients ein Versprechen vom Server zurückgeben, das entweder erfolgreich aufgelöst oder abgelehnt werden kann.

Wir ergänzen die Funktion mit folgendem Code:

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

Hier können Sie darauf achten, dass blob als responseType . Bevor wir weiter gehen, lassen Sie uns darüber sprechen, was es ist.

Blob-Objekte


Blob ist ein unveränderliches Objekt, das einige Rohdaten darstellt. Solche Objekte werden häufig verwendet, um mit Daten zu arbeiten, die möglicherweise nicht im nativen JavaScript-Format vorliegen. Solche Objekte sind Folgen von Bytes, in denen beispielsweise Dateidaten gespeichert sind. Es scheint, dass das Blob Objekt einen Link zu einer Datei speichert, dies ist jedoch nicht der Blob . Diese Objekte speichern Daten, mit denen Sie arbeiten können. Zum Beispiel können sie in Dateien gespeichert werden.

Projektabschluss


.then() wir nun wissen, was Blob Objekte sind, können wir einen anderen .then() , res.data neues Blob Objekt basierend auf res.data zu erstellen. Beim Erstellen dieses Objekts übergeben wir einen Parameter an seinen Konstruktor, der angibt, dass die vom Objekt gespeicherten Daten vom Typ application/pdf . Danach können wir die saveAs() -Methode verwenden, die aus dem saveAs() importiert wurde, und die Daten in einer Datei speichern. Infolgedessen createAndDowndloadPdf der Code der Methode createAndDowndloadPdf wie folgt aus:

   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');      })  } 

So sieht die Browser-Oberfläche aus.


Anwendung im Browser

Nachdem Sie die Felder ausgefüllt und auf die Schaltfläche Download PDF geklickt haben, werden die Daten auf den Server übertragen und ein PDF-Dokument von diesem heruntergeladen.


PDF herunterladen

Und hier ist das PDF-Dokument selbst.


Software-generiertes PDF

Zusammenfassung


Wir haben uns einen Mechanismus angesehen, mit dem Sie programmgesteuert PDF-Dateien erstellen können. Hier ist ein GitHub-Repository mit Projektcode. Wir hoffen, dass die Ideen, die Sie in diesem Material getroffen haben, in Ihrer Entwicklung Anwendung finden.

Liebe Leser! Wie würden Sie das Problem der programmgesteuerten Erstellung von PDF-Dateien mit Node.js lösen?

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


All Articles