Buat PDF dinamis menggunakan Bereaksi dan Node.js

Bahan, terjemahan yang kami terbitkan hari ini, dikhususkan untuk pembuatan file-file PDF dinamis menggunakan kode-HTML sebagai templat. Yaitu, kita akan berbicara tentang cara membuat faktur sederhana untuk pembayaran barang atau jasa tertentu, data dinamis yang termasuk di dalamnya diambil dari keadaan aplikasi Bereaksi. Basis aplikasi Bereaksi dibuat menggunakan create-react-app, bagian server dari proyek didasarkan pada Node.js, dan kerangka kerja Express digunakan dalam pengembangannya.



Penulis materi ini mencatat bahwa ia menyiapkan video yang menunjukkan perkembangan proyek. Jika Anda memutuskan untuk menonton video dan membaca artikel, disarankan untuk melakukannya. Pertama, membaca sepintas lalu artikel tersebut, lalu nyalakan video dan buat ulang sistem yang Anda pertimbangkan di sana. Setelah itu, baca saja artikelnya.

Pembuatan proyek


Buat direktori proyek dan buka:

mkdir pdfGenerator && cd pdfGenerator 

Buat aplikasi Bereaksi baru:

 create-react-app client 

Setelah menyelesaikan pembuatan aplikasi, buka direktori yang baru saja Anda buat dan instal dependensi:

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

Buat server Express. Untuk melakukan ini, buat folder server di direktori proyek dan pergi ke sana. Di dalamnya, buat file index.js dan mulai inisialisasi proyek:

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

Di sini, untuk membentuk package.json , cukup tekan Enter beberapa kali. Setelah itu, jalankan perintah berikut untuk menambahkan dependensi yang diperlukan ke bagian server proyek:

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

Sekarang, di file client/package.json , di atas bagian deskripsi dependensi, tambahkan berikut ini:

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

Ini akan membantu bekerja dengan server lokal dari kode klien.

Sekarang Anda perlu membuka dua jendela terminal.

Di jendela pertama, buka direktori client dan jalankan perintah berikut:

 npm start 

Di jendela kedua, buka folder server dan jalankan perintah berikut:

 nodemon index.js 

Penyiapan klien awal


Bagian klien dari proyek kami akan terlihat sangat sederhana.

Pertama, di file src/App.js bagian klien dari aplikasi, kami mengimpor dependensi ke dalam kode:

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

Setelah itu, di bagian atas deskripsi komponen, inisialisasi keadaan:

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

Mari kita hapus markup JSX standar yang dibuat dalam templat aplikasi menggunakan create-react-app dan kembali dari metode render() . Masukkan yang berikut ini ke dalamnya:

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

Mari kita buat metode handleChange , yang akan bertanggung jawab untuk memperbarui data status aplikasi yang terkait dengan bidang input:

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

Sekarang kita dapat beralih ke tugas membuat file PDF. Bagian itu, yang diselesaikan dengan cara klien, adalah membuat permintaan POST ke server. Permintaan mengirim status aplikasi:

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

Sebelum kami terus bekerja di sisi klien proyek, kami perlu mengkonfigurasi rute di server. Ini akan memungkinkan server untuk menerima data dari klien, menghasilkan file PDF dan mentransfer file ini kembali ke klien.

Penyiapan server awal


Bagian server dari proyek hanya akan mencakup dua rute. Diperlukan satu untuk membuat PDF. Yang kedua adalah untuk mengirim file ke klien.

Pertama, impor dependensi ke file index.js :

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

Kami menginisialisasi aplikasi Express dan mengkonfigurasi port:

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

Menyiapkan parser kueri. Apa yang kita butuhkan akan tersedia sebagai req.body . Kami juga akan mengonfigurasi CORS sehingga kesalahan Cross-Origin Request Blocked kami tidak mengganggu pekerjaan kami:

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

Setelah itu, jalankan server:

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

Sekarang kita dapat menangani kode yang bertanggung jawab untuk membuat PDF.

Buat template HTML untuk PDF


Kami membutuhkan template HTML untuk digunakan saat membuat file PDF. Dalam membuat templat seperti itu, kemungkinan tak terbatas membuka di hadapan kita. Segala sesuatu yang dapat dibuat menggunakan HTML dan CSS murni dapat direpresentasikan sebagai file PDF. Buat direktori documents di folder server , buka dan buat file index.js di dalamnya:

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

Dari file ini, kami mengekspor fungsi panah yang akan mengembalikan semua kode HTML yang diperlukan. Saat memanggil fungsi ini, Anda dapat menggunakan parameter, yang juga akan kami jelaskan di sini.

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

Di sini saya akan memberi Anda contoh template HTML, dan Anda cukup menyalinnya ke proyek Anda. Tetapi Anda, tentu saja, dapat membuat template Anda sendiri.

Mari kita index.js kode index.js dari folder server/documents ke formulir berikut:

 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 menyertakan file ini dalam file server/index.js :

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

Buat PDF


Ingatlah bahwa di server kita akan membuat dua rute. Rute POST akan bertanggung jawab untuk menerima data dari klien dan membuat file PDF. Rute GET akan digunakan untuk mengirim file yang sudah selesai ke klien.

▍ buat-pdf rute


Dalam rute POST create-pdf , kita akan menggunakan perintah pdf.create() , merujuk pada objek yang diimpor dari modul html-pdf .

Sebagai parameter pertama metode pdf.create() , templat HTML digunakan, serta data yang diterima dari klien.

Untuk mengembalikan pdf.create() , kami memanggil metode toFile() , memberikannya nama yang ingin kami tetapkan ke dokumen PDF, serta fungsi panah panggilan balik. Fungsi ini, jika terjadi kesalahan, akan menjalankan perintah res.send(Promise.reject()) . Jika semuanya berjalan dengan baik, dia akan menjalankan perintah 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());    }); }); 

▍Mengambil-pdf Rute


Pada saat yang sama, kami akan membuat rute yang akan digunakan setelah, atas permintaan klien, file PDF berhasil dibuat. Di sini kita hanya mengambil dokumen yang sudah jadi dan mengirimkannya ke klien menggunakan res.sendFile() :

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

Fungsi klien membuatAndDownloadPdf


Sekarang kita dapat kembali ke kode klien dan terus bekerja pada fungsi createAndDownloadPdf . Di sini kita membuat permintaan POST ke server menggunakan modul axios . Sekarang fungsi ini terlihat seperti ini:

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

Jika setelah menjalankan permintaan POST ke server, dokumen PDF telah dibuat, kami perlu menjalankan permintaan GET, sebagai respons server akan mengirim dokumen yang sudah selesai ke klien.

Untuk menerapkan skema perilaku ini, kami, setelah memanggil axios.post() , memanggil. then() . Ini memungkinkan kami melakukan itu sebagai tanggapan terhadap permintaan POST klien, kami mengembalikan janji dari server yang dapat berhasil diselesaikan atau ditolak.

Kami melengkapi fungsi dengan kode berikut:

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

Di sini Anda dapat memperhatikan fakta bahwa blob digunakan sebagai jenis responseType . Sebelum kita melangkah lebih jauh, mari kita bicara tentang apa itu.

Benda gumpalan


Blob adalah objek abadi yang mewakili beberapa data mentah. Objek seperti itu sering digunakan untuk bekerja dengan data yang mungkin tidak dalam format JavaScript asli. Objek tersebut adalah urutan byte yang menyimpan, misalnya, file data. Tampaknya objek Blob menyimpan tautan ke file, tetapi kenyataannya tidak. Objek-objek ini menyimpan data yang dapat Anda gunakan. Misalnya - mereka dapat disimpan ke file.

Penyelesaian Proyek


Sekarang kita tahu apa objek Blob , kita bisa menggunakan panggilan .then() untuk res.data objek Blob baru di dalamnya, berdasarkan res.data. Saat membuat objek ini, kami akan meneruskan parameter ke konstruktornya, menunjukkan bahwa data yang akan disimpan oleh objek adalah dari type application/pdf . Setelah itu, kita dapat menggunakan metode saveAs() , yang diimpor dari modul file-saver , dan menyimpan data ke file. Akibatnya, kode metode createAndDowndloadPdf akan terlihat seperti yang ditunjukkan di bawah ini:

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

Seperti inilah tampilan antarmuka browser.


Aplikasi di browser

Setelah mengisi kolom dan mengklik tombol Download PDF , data ditransfer ke server dan dokumen PDF diunduh dari sana.


Unduh PDF

Dan di sini adalah dokumen PDF itu sendiri.


Perangkat Lunak Menghasilkan PDF

Ringkasan


Kami melihat mekanisme yang memungkinkan Anda membuat file PDF secara terprogram. Ini adalah repositori GitHub dengan kode proyek. Kami berharap bahwa ide-ide yang Anda temui dalam materi ini akan menemukan aplikasi dalam pengembangan Anda.

Pembaca yang budiman! Bagaimana cara Anda menyelesaikan masalah pemrograman file PDF menggunakan Node.js?

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


All Articles