如何使用JavaScript模板快速生成PDF文档?

发表时间: 2022-08-25 09:21

在您的 js 应用程序中使用 eDocGen 从 JSON/XML/Database 创建 PDF 文档的指南。

文档生成是开发人员生活中非常普遍的需求。无论是电子商务网站、管理应用程序还是其他任何东西。它可以是发票生成、保险文件准备、医生处方、人力资源报价生成、工资单生成,你可以想到大量的用例。总是需要生成文档。

从开发人员的角度来看,有几种常见的方法可以完成这项工作。

  1. 创建 HTML 元素并打印它们以生成文档
  2. 使用一些库来生成文档
  3. 让服务器处理基于静态模板的文档生成

这些方法对我没有帮助。客户希望自己定制他们的文件。我一直在寻找一种方法,发现eDocGen是一种单点解决方案。

与其他服务不同,eDocGen 提供了可以集成到我们应用程序中的 RestAPI。

在本文中,我们将讨论如何将 eDocGen 集成到我们的 js 应用程序中,以从各种数据格式(如 JSON/XML/Database 模式)生成文档。请免费试用以开始编码。

让我们潜入并编写代码。

项目设置

出于演示目的,我创建了一个在 nodejs 上运行的示例 js 应用程序。

请按照以下步骤为我们设置编码游乐场。

步骤1:

用于npm init创建 package.json

第2步:

添加axios, form-data, request,xhr2开发此应用程序所需的依赖项npm install axios form-data request xhr2

第 3 步:

我们需要一个索引文件作为我们应用程序的起点。在根目录中创建一个 index.js 文件并修改 package.json 如下所示。

JSON

scripts": {    "start": "node index.js"  }

现在我们有一个基本的应用程序可以开始。这些步骤结束后,package.json 应该如下所示。

JSON

{  "name": "nodejs-multiple-upload-files",  "version": "1.0.0",  "main": "index.js",  "scripts": {    "start": "node index.js"  },  "dependencies": {    "axios": "^0.27.2",    "form-data": "^4.0.0",    "request": "^2.88.2",    "xhr2": "^0.2.1"  }}

登录

虽然这篇文章是关于文档生成的,但我们需要登录才能获取我们的访问令牌。这是一个典型的JWT令牌,将用于授权文档生成 API。

JavaScript

var XMLHttpRequest = require("xhr2");var xhr = new XMLHttpRequest();module.exports.getToken = function (callback) {  var data = JSON.stringify({    username: "<your username>",    password: "<password>",  });  xhr.addEventListener("readystatechange", function () {    if (this.readyState === 4) {      token = JSON.parse(this.responseText).token;      console.log("User Token", token);      callback(token);    }  });  xhr.open("POST", "https://app.edocgen.com/login");  xhr.setRequestHeader("content-type", "application/json");  xhr.setRequestHeader("cache-control", "no-cache");  xhr.send(data);};

我们可以将令牌在应用程序中缓存一个小于过期时间的时间段,并使用它来生成文档或上传模板。到期时间过后,我们可以刷新令牌。缓存可以是 Redis 或内存缓存。这取决于您的应用程序设计。

模板设计

如上所述,eDocGen 允许用户自定义和上传模板。但是如何动态映射数据呢?有一些将数据映射到文档的规则。我们将看到如何使用规则创建模板。

看看这个文件。

eDocGen{}对动态字段使用由 括起来的标签。我们可以动态添加文字、logo、表格、条件语句、数学计算等。

例如,在上图中,

字符串字段: {Invoice_Number}{Invoice_Date}配置为替换为模板中的文本。模板中 {} 内的任何内容都将与输入数据匹配并替换。

动态表: 当表中存在需要循环和替换的数据数组时,动态表将是一个不错的选择。表中的行以 开头{#tablename}和结尾{/tablename}。在上面的示例中,发票表中的一行在第一列以 {#IT} 开头,在最后一列以 {/IT} 结尾。行中的列可以有字符串字段。在我们的示例中,{Item_description}并且{Amount}

图片: eDocGen 提供动态添加图片到模板的功能。请按照以下步骤操作。

  • 将图像上传到应以 image_id 响应的 eDogGen。
  • {%image_id}是用于填充图像的标签。图像image_id将从 eDocGen 存储中获取并替换为{%image_id}. 预计image_id将出现在输入数据中。

基于条件的动态字段(If-Else):可以使用条件标签有条件地显示内容。例如,当语言为英语时,文档中会显示{#language == "english"} 英语内容。同样,单个文档模板可以支持多种语言。

数学计算: eDocGen 支持基于模板中定义的公式的数学计算。可以使用以下公式计算发票中项目金额的总和。

JSON

{    IT // array of items    | summation:'Amount' // value that needs to be used for calculation     | format_number: ",” // format of the value}

请前往JSON-to-pdf了解更多详情。

模板上传

准备好模板后,就可以将其上传以供使用。有两种方法。

  1. eDocGen 的交互式 UI - 与 Dropbox、驱动器、Evernote 集成
  2. eDocGen 的 RestAPI - 可以集成到客户端代码中以上传模板。

对于演示,我使用 UI 来上传模板。成功上传后,我们会得到一个 ID 作为响应。这是将用于生成文档的 ID。

如果您希望使用 API,请在此处留下 Upload API 结构供您参考。

JSON

"/api/v1/document": {  "post": {    "tags": [      "Document"    ],    "description": "Upload template to eDocGen",    "produces": [      "application/json"    ],    "consumes": [      "multipart/form-data"    ],    "parameters": [      {        "name": "documentFile",        "description": "file to upload",        "required": true,        "type": "file",        "in": "formData"      },      {        "name": "x-access-token",        "in": "header",        "description": "JWT auth token from login",        "required": true,        "type": "string"      }    ],    "responses": {      "200": {        "description": "Successfully uploaded document file"      },      "other": {        "description": "Operation failed"      }    }  }}

JSON 到文档生成

现在我们准备好了模板。让我们生成文档。

文档生成有两个阶段。

  1. 请求生成文档
  2. 下载文件

第 1 步:请求生成文档

我们要求生成包含所需详细信息的文档,并得到确认。该过程异步发生在屏幕后面。

文档生成所需的参数

应用程序接口:POST-/api/v1/document/generate/bulk

请求正文

表格数据

文档 ID

模板的id

格式

pdf/docx(模板应支持格式)

输出文件名

输出文件的文件名。

输入文件

该文件包含标记值。支持 json、xlsx 和 xml。

标题

内容类型

多部分/表单数据

x-访问令牌

来自登录的 JWT 身份验证令牌

输入数据

inputFile 中的数据应该是模板定义的结构。例如,对于上面的模板映射将如下所示。

  • Invoice_Number在 JSON 中应该与{Invoice_Number}模板中的匹配。
  • 对于表数据,它应该是一个对象数组,带有Item_DescriptionAmount.
  • 金额应该是一个用于求和计算的数字。

第 2 步:下载文件

可以使用从上述步骤中获得的输出 ID 和输出文件的名称下载生成的文档。

我们将在这里使用两个 API。

  1. 了解文件存在的 API:/api/v1/output/name/${fileName}
  2. 下载文件的API:/api/v1/output/download/${outputId}

由于文档生成是异步发生的,要知道文档是否生成,我们将使用/api/v1/output/nameapi。

来自 API 的成功响应/api/v1/output/name将下载文件。

我将这两个步骤组合在一个 js 文件中,如下所示。

爪哇

let login = require("../edocgen_login");const fs = require("fs");const uuid = require("uuid");const FormData = require("form-data");let axios = require("axios");let fileName = uuid.v4();const headers = {  "Content-Type": "multipart/form-data",  "x-access-token": "null",};const hostName = "https://app.edocgen.com/api/v1/document/generate/bulk";const outputFormat = "<format>";// pdf / docxconst documentId = "<template_id>";    // id of the template we want to usemodule.exports.generateFiles =  function () {  let authToken = login.getToken(function handleUsersList(token) {    headers["x-access-token"] = token;    var formBody = new FormData();    formBody.append("documentId", documentId);    formBody.append("format", outputFormat);    formBody.append("outputFileName", fileName);    // json data for the template    formBody.append("inputFile", fs.createReadStream("./JSON_Data_Single.json"));   // local path forjson file    let config = {      method: "post",      url: hostName,      headers: headers,      data: formBody,    };    console.log(`https://app.edocgen.com/api/v1/output/name/${fileName}.${outputFormat}`);    let config_output = {      method: "get",      url:`https://app.edocgen.com/api/v1/output/name/${fileName}.${outputFormat}`,      headers: headers,    };    const MAX_RETRY = 50;    let currentRetry = 0;    // max retry for 50 times    function errorHandler() {      if (currentRetry < MAX_RETRY) {        currentRetry++;        console.log("Document is not prepared yet! Retrying...");        sendWithRetry(processResponse);      } else {        console.log("No luck. Document is not generated. Retried multiple times.");      }    }        // sendWithRetry checks for file existence    // on success, it proceeds to download the file    // on failure, it retries     // todo: introduce spin lock    function sendWithRetry(callback) {      axios(config_output)        .then(function (response) {          if (response.data.output.length !== 1) {            throw new axios.Cancel("Document is not found. Throw error.");          } else {            callback(response);          }        })        .catch(errorHandler);    }    axios(config)      .then(function (response) {        sendWithRetry(processResponse);      })      .catch(function (error) {        console.log(error);      });  });};function processResponse(response) {  const outputId = response.data.output[0]._id;  console.log(    "Output Document is Generated. Id = ",    response.data.output[0]._id  );  let config_download = {    method: "get",    url: `https://app.edocgen.com/api/v1/output/download/${outputId}`,    headers: headers,    responseType: "arraybuffer",  };  axios(config_download)    .then(function (response) {      console.log("Output file is downloaded " + `${fileName}.${outputFormat}`);      fs.writeFileSync(`./${fileName}.${outputFormat}`, response.data);    })    .catch(function (error) {      console.log("Error while downloading");      console.log(error);    });}

单个与多个文档

当数据为单个 JSON 时,将生成给定格式的单个文档。

当数据是对象数组时,将生成每个数组元素的文档并将其压缩到文件中。

XML 到文档生成

XML 数据的过程很简单。我们需要做的就是传递 XML 文件来代替 JSON 数据。

就像JSON to documentXML to Document 我们也需要documentId, outputFileName, format and inputFile。除输入文件外,与 JSON 相同的所有内容都将是 XML 文件。

示例 XML 数据如下所示

XML

<?xml version="1.0" encoding="UTF-8" ?><marker>  <values>    <Invoice_Number>SBU-2053501</Invoice_Number>    <Invoice_Date>31-07-2020</Invoice_Date>    <Terms_Payment>Net 15</Terms_Payment>    <Company_Name>ABC company</Company_Name>    <Billing_Contact>ABC-Contact1</Billing_Contact>    <Address>New york, United State</Address>    <Email>support@edocgen.com</Email>	<Logo>621cd2b783a6095d7b15a443</Logo>      <Sum1>6,751</Sum1>	 <para>61b334ee7c00363e11da3439</para>    <ITH>      <Heading1>Item Description</Heading1>      <Heading2>Amount</Heading2>    </ITH>    <IT>      <Item_Description>Product Fees: X</Item_Description>      <Amount>5,000</Amount>    </IT>  </values><marker>

我为 XML 作为数据源所做的代码更改很简单,如下所示

JavaScript

var formBody = new FormData();formBody.append("documentId", documentId);formBody.append("format", outputFormat);formBody.append("outputFileName", fileName);formBody.append("inputFile", fs.createReadStream("./XML_Invoice.xml"));

数据库到文档生成

从数据库生成文档几乎与其他数据源相同。但在这种情况下,我们需要提供连接详细信息和 SQL 查询,而不是上传 inputFile。

SQL 查询的输出列应与文档模板中的标签匹配。

让我们看看如何在代码中进行配置。

JavaScript

const templateId = "<template id>";const dbVendor = "mysql";const dbUrl = "<jdbc connection URL>";const dbLimit = "100";const dbPassword = "<database password>";const dbQuery = "SELECT JSON_ARRAY(first, last) FROM customers;";const outputFormat = "pdf";// form data prepareationlet formBody = new FormData();formBody.append("documentId", templateId);formBody.append("format", outputFormat);formBody.append("dbVendor", dbVendor);formBody.append("dbUrl", dbUrl);formBody.append("dbLimit", dbLimit);formBody.append("dbPassword", dbPassword);formBody.append("dbQuery", dbQuery);formBody.append("outputFileName", fileName);

其他一切都将保持不变。

通过电子邮件发送文档

eDocGen 提供了通过电子邮件发送生成的文档的功能。

文档生成所需的参数

应用程序接口:POST-/api/v1/output/email

请求正文

JSON

出局

将需要通过电子邮件发送的输出 ID 放在这里

电子邮件ID

将用户电子邮件放在这里

标题

内容类型

多部分/表单数据

x-访问令牌

来自登录的 JWT 身份验证令牌

代码示例

let login = require("../edocgen_login");let axios = require("axios");const hostName = "https://app.edocgen.com/api/v1/output/email";const headers = {  "Content-Type": "application/json",  "x-access-token": "null",};const outId = "<output ID>"; // Put output ID here which need to be sent via emailconst emailId = "<user email>"; // Put user email heremodule.exports.generateFiles = function () {  let authToken = login.getToken(function handleUsersList(token) {    headers["x-access-token"] = token;    let payload = { outId: outId, emailId: emailId };    let config = {      method: "post",      url: hostName,      headers: headers,      data: payload,    };    axios(config)      .then(function (response) {        console.log("Mail sent");      })      .catch(function (error) {        console.log(error);      });  });};

来自 eDocGen 的电子邮件如下所示

还有很多其他的功能我在这里无法涵盖。但我希望这篇文章可以为您提供一个从哪里开始的想法。