该材料(我们今天发布的翻译)致力于使用HTML代码作为模板来创建动态PDF文件。 即,我们将讨论如何创建用于支付某些商品或服务的简单发票,其中包含的动态数据取自React应用程序的状态。 React应用程序的基础是使用create-react-app创建的,项目的服务器部分基于Node.js,Express框架用于其开发中。

该材料的作者指出,他准备了一个
视频来演示该项目的发展。 如果您决定观看视频并阅读文章,建议您这样做。 首先,浏览文章,然后打开视频并重新创建您正在考虑的系统。 之后,只需阅读文章即可。
项目创建
创建一个项目目录并转到它:
mkdir pdfGenerator && cd pdfGenerator
创建一个新的React应用程序:
create-react-app client
完成应用程序创建之后,转到您刚刚创建的目录并安装依赖项:
cd client && npm i -S axios file-saver
创建一个Express服务器。 为此,请在项目目录中创建
server
文件夹,然后转到该文件夹。 在其中创建
index.js
文件并开始项目初始化:
mkdir server && cd server && touch index.js && npm init
在这里,要形成
package.json
,只需按几次
Enter
。 之后,执行以下命令以将必要的依赖项添加到项目的服务器部分:
npm i -S express body-parser cors html-pdf
现在,在
client/package.json
文件的依赖项描述部分上方,添加以下内容:
"proxy": "http://localhost:5000/"
这将有助于从客户端代码使用本地服务器。
现在您需要打开终端的两个窗口。
在第一个窗口中,转到
client
目录并执行以下命令:
npm start
在第二个窗口中,转到
server
文件夹并执行以下命令:
nodemon index.js
初始客户端设置
我们项目的客户部分看起来非常简单。
首先,在应用程序客户端部分的
src/App.js
,我们将依赖项导入代码中:
import axios from 'axios'; import { saveAs } from 'file-saver';
之后,在组件描述的顶部,初始化状态:
state = { name: 'Adrian', receiptId: 0, price1: 0, price2: 0, }
让我们删除使用
create-react-app
在应用程序模板中
create-react-app
并从
render()
方法返回的标准JSX标记。 将以下内容插入其中:
<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>
让我们创建
handleChange
方法,该方法将负责更新与输入字段相关的应用程序状态数据:
handleChange = ({ target: { value, name }}) => this.setState({ [name]: value })
现在,我们可以继续创建PDF文件的任务。 其中的一部分(通过客户端解决)是向服务器创建POST请求。 该请求发送应用程序状态:
createAndDownloadPdf = () => { axios.post('/create-pdf', this.state) }
在继续进行项目客户端之前,我们需要在服务器上配置路由。 这将允许服务器从客户端接收数据,生成PDF文件并将该文件传输回客户端。
初始服务器设置
项目的服务器部分将仅包括两条路线。 需要一个来创建PDF。 第二个是用于将文件发送到客户端。
首先,将依赖项导入到
index.js
文件中:
const express = require('express'); const bodyParser = require('body-parser'); const pdf = require('html-pdf'); const cors = require('cors');
我们初始化Express应用程序并配置端口:
const app = express(); const port = process.env.PORT || 5000;
设置查询解析器。 我们需要的将以
req.body
形式提供。 我们还将配置CORS,以便我们的
Cross-Origin Request Blocked
错误不会干扰我们的工作:
app.use(cors()); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json());
之后,启动服务器:
app.listen(port, () => console.log(`Listening on port ${port}`));
现在我们可以处理负责创建PDF的代码。
为PDF创建HTML模板
创建PDF文件时,我们需要使用HTML模板。 在创建这样的模板时,我们面前蕴藏着无限的可能性。 可以使用纯HTML和CSS创建的所有内容都可以表示为PDF文件。 在
server
文件夹中创建
documents
目录,转到该目录并在其中创建
index.js
文件:
mkdir documents && cd documents && touch index.js
从该文件中,我们导出一个箭头函数,该函数将返回所有必需的HTML代码。 调用此函数时,可以使用参数,我们还将在此处进行描述。
module.exports = ({ name, price1, price2, receiptId }) => { ... }
在这里,我将为您
提供一个 HTML模板
的示例 ,您只需将其复制到您的项目中即可。 但是您当然可以创建自己的模板。
让我们
index.js
server/documents
文件夹中
index.js
代码转换为以下格式:
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
将此文件包含在
server/index.js
:
const pdfTemplate = require('./documents');
创建PDF
回想一下,我们将在服务器上创建两条路由。 POST路由将负责从客户端接收数据并创建PDF文件。 GET路由将用于将完成的文件发送到客户端。
▍create-pdf路线
在
create-pdf
POST路由中,我们将使用
pdf.create()
命令,引用从
html-pdf
模块导入的对象。
作为
pdf.create()
方法的第一个参数,将使用HTML模板以及从客户端接收的数据。
要返回
pdf.create()
,我们调用
toFile()
方法,并为其传递我们要分配给PDF文档的名称以及箭头回调函数。 发生错误时,此函数将执行
res.send(Promise.reject())
命令。 如果一切顺利,她将执行
res.send(Promise.resolve())
命令
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()); }); });
etch获取-pdf路线
同时,我们将创建一条路由,该路由将在应客户端请求成功创建PDF文件之后使用。 在这里,我们只是获取完成的文档,然后使用
res.sendFile()
将其发送给客户端:
app.get('/fetch-pdf', (req, res) => { res.sendFile(`${__dirname}/result.pdf`) })
客户端功能createAndDownloadPdf
现在,我们可以返回客户端代码,并继续使用
createAndDownloadPdf
函数。 在这里,我们使用
axios
模块向服务器发出POST请求。 现在,该函数如下所示:
createAndDownloadPdf = () => { axios.post('/create-pdf', this.state) }
如果在对服务器执行POST请求之后创建了PDF文档,则我们需要执行GET请求,作为响应,服务器会将完成的文档发送给客户端。
为了实现此行为方案,我们在调用
axios.post()
之后进行调用。
then()
。 这使我们能够响应客户的POST请求,从服务器返回一个可以成功解决或拒绝的承诺。
我们用以下代码补充该功能:
.then(() => axios.get('/fetch-pdf', { responseType: 'blob' })) .then(( res) => {})
在这里,您可以注意
blob
用作
responseType
的事实。 在继续之前,让我们谈谈它的含义。
Blob对象
Blob是代表某些原始数据的不可变对象。 此类对象通常用于处理可能不是本机JavaScript格式的数据。 这样的对象是存储例如文件数据的字节序列。 似乎
Blob
对象正在存储指向文件的链接,但实际上并非如此。 这些对象存储您可以使用的数据。 例如-它们可以保存到文件中。
项目完成
现在我们知道了
Blob
对象是什么,我们可以使用另一个
.then()
调用基于res.data在其中
res.data
新的
Blob
对象。 创建此对象时,我们会将一个参数传递给它的构造函数,指示该对象将存储的数据为
application/pdf
类型。 之后,我们可以使用从
file-saver
保存器模块导入的
saveAs()
方法,并将数据保存到文件中。 结果,
createAndDowndloadPdf
方法的代码如下所示:
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'); }) }
这是浏览器界面的外观。
浏览器中的应用填写字段并单击“
Download PDF
按钮后,数据将传输到服务器,并从服务器下载PDF文档。
下载PDF这是PDF文档本身。
软件生成的PDF总结
我们研究了一种允许您以编程方式创建PDF文件的机制。
这是一个包含项目代码
的 GitHub存储库。 我们希望您在本材料中遇到的想法能够在您的开发中找到应用。
亲爱的读者们! 您将如何解决使用Node.js以编程方式创建PDF文件的问题?