基於前端技術生成PDF方案

需求背景

  • 業務系統須要預覽報告(如產品週報,體檢報告等)並生成pdf格式供用戶下載,或者按期發送給指定用戶
  • 報告格式相對固定,由文本,圖片和圖表組成,基本與前端頁面保持一致

解決方案

需求分爲兩步:報告預覽和報告生成。html

  • 報告預覽在前端進行展現,可以使用前端技術,如React/Vue等技術棧對其進行還原,數據從服務端獲取。
  • 報告生成須要對第一步生成的HTML進行PDF的轉換生成,HTML2PDF的方式又分爲兩種:
    • 基於canvas的客戶端生成方案
    • 基於nodejs + puppeteer的服務端生成方案

一個完整的案例

下面以一個體檢報告的案例進行這兩種方案的說明: 體檢報告展現形式以下,格式相對固定,分爲四個頁面:我的信息頁,建議頁,原理頁,我的信息頁與建議頁數據來源於服務器。 前端

報告樣式

基於canvas的客戶端生成方案

canvas是HTML5標準中新增的元素,可用於經過使用JS的腳原本繪製圖形。canvas提供了toDataURL/toBlob方法,用於把canvas中的內容轉換爲圖片,API文檔以下(來源於MDN): node

canvas api

因爲HTML文檔再瀏覽器中是以DOM樹的形式存在,因此咱們能夠經過三步完成HTML到PDF的轉換:nginx

  • 將DOM樹轉換爲canvas對象,可以使用html2canvas完成
  • 將canvas轉換爲圖片,可以使用canvas.toDataURL完成
  • 將圖片轉換爲PDF,可以使用jsPDF完成

完整代碼實現:github.com/simonwoo/di…git

截圖以下,點擊下載按鈕可進行pdf生產:github

截圖

該方案徹底基於客戶端的方式生成,不須要服務器進行支持。在使用該方案的過程當中,發現了一些問題:web

  • 生產的PDF比較模糊,質量不高
  • 若是HTML中有外鏈圖片,沒法生成
  • 因爲第一步是經過DOM去生成canvas,因此針對特別長的報告,DOM還沒有加載完便點擊下載時,會形成報告生成問題
  • 由於是客戶端方案,因此須要用戶主動觸發生成,但對於一些按期發送給用戶的報告,該方案沒法使用

基於nodejs + puppeteer的服務端生成方案

puppeteer是google推出的headless瀏覽器,即沒有圖形界面的瀏覽器,但又能夠實現普通瀏覽器HTML/JS/CSS的渲染,以及其餘基本瀏覽器功能。你能夠理解爲一個沒有界面的Chrome瀏覽器。主要有如下幾種使用場景:ajax

  • 生成頁面的截圖和PDF
  • 抓取SPA並生成預先呈現的內容(即「SSR」)
  • 爬蟲,從網站抓取你須要的內容
  • 自動化測試,自動錶單提交,UI測試,鍵盤輸入等
  • 建立一個最新的自動化測試環境。使用最新的JavaScript和瀏覽器功能,直接在最新版本的Chrome中運行測試 經過理解puppeteer的功能,咱們能夠開啓一個實例去渲染HTML報告,而後再利用其提供的轉換PDF功能進行PDF的生成。

兩個重要的API:npm

  • page.goto(url, [options]) - 打開指定url的文件,能夠是本地文件(file://)也能夠是網絡文件(http://)
  • page.pdf([options]) - 轉換頁面成PDF文件

puppeteer使用一個小例子,將百度網頁轉換爲pdf:canvas

puppeteer使用

完整代碼以下:

項目啓動流程以下:

  • 進入到webapp目錄,使用npm install和npm run start啓動前端服務器,地址爲:localhost:3000
  • 進入到server目錄,使用npm install和npm run dev啓動node服務器,地址爲:localhost:7001

整個服務架構以下:

架構圖

node服務器經過路由增長一個pdf生成的controller,該controller經過啓動puppeteer實例去加載localhost:3000的頁面並生成pdf。直接在瀏覽器中經過http://localhost:7001/pdf便可訪問到生成的pdf.

在實際環境中,前端頁面可部署在nginx服務器上或者直接放在Node服務器上,puppeteer也支持使用cookie的操做,這樣能夠避免一些須要身份認證的問題。

相比客戶端生成方式,使用puppeteer生成的pdf質量比較高,可知足生產要求。

本文中提到的兩種方案中,均省去了ajax後端請求數據部分,讀者可根據須要自行增長。

Reference

相關文章
相關標籤/搜索