更新 : 2019-06-27 css
後端製做好 pdf 後,下一個工做就是讓前端下載或者 print 了. html
下載能夠用 <a href="123.pdf" download="abc.pdf" > 的方式前端
print 的話, 能夠用 iframehtml5
const a = document.createElement('a'); a.href = '123.pdf'; a.download = 'abc.pdf'; a.click(); const iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.src = 'abc.pdf'; iframe.onload = () => { iframe.focus(); iframe.contentWindow!.print(); };
比較常遇到的問題是跨域, node
iframe 不支持 allow origin 因此咱們不能夠像處理 ajax 那樣去跨域webpack
<a> 不會出現 error 可是不會下載只會開多一個 new tabgit
一個通用的方法是先用 ajax + allow origin 去把 pdf 拿下來. 獲取到 Blob github
而後用 createObjectUrl 來實現web
const result = await this.httpClient.get('http://192.168.1.152:61547/123.pdf', { observe: 'response', responseType: 'blob' }).toPromise(); const url = window.URL.createObjectURL(result.body); const a = document.createElement('a'); a.href = url; a.download = '123.pdf'; a.click(); const iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.src = url; iframe.onload = () => { iframe.focus(); iframe.contentWindow!.print(); };
這樣就是本地資源了. ajax
針對 <a> 還有一個方法就是不要使用靜態資源路徑, 你改爲好比... /api/download-file 而後後端返回 file 是能夠下載的哦.
refer :
https://github.com/crabbly/Print.js/issues/95
http://www.javashuo.com/article/p-zmslogqt-hb.html
作企業項目,常常會須要保存或輸出 pdf 格式。
好比 invoice, purchase order 等等。
pdf 是 90 年 adobe 公司設計的, 08 年開放.
製做 pdf 不難,可是互聯網時代,大多數文本都用 html 格式來寫.
要弄一份如出一轍的 pdf 出來就 double work 了。
因而就有了 html to pdf 的需求。
市場上已經有好多公司作這個出來賣錢了。
好比:
https://www.nrecosite.com/pdf_generator_net.aspx#
https://www.paperplane.app/pricing
價格都不便宜呢.
html 是 93 年問世的, 很長一段時間, 遊覽器解析引擎都是閉源的. 因此要作 html to pdf 是蠻累的,要解析 html css。
直到 04 年蘋果開源了 safari 的引擎 webkit。
就有人基於 webkit 作出了 webkit html to pdf
只要安裝 webkit 而後經過 commond 調用一下就能夠把 html 變成 pdf 文件了,開源的哦。
.net https://github.com/rdvojmoc/DinkToPdf 就是基於 wkhtmltopdf 的封裝
有了前車可鑑, 後來的 phantomjs 也有 html to pdf 的功能
jsreport 早年就是基於 phantomjs 實現的 to html pdf 功能
https://github.com/jsreport/jsreport
https://github.com/jsreport/jsreport-dotnet (.net 版本)
一直到 2017 年, chromium 推出了 headless 版.
chromium 是 google 基於 webkit 的 folk, 如今 chrome 用着的是 blink, chromuim 算是前身吧.
這個 chromium headless 可厲害了, google 出品, phantomjs 做者隨後就宣佈不在維護了,讓位給 chromium.
chromium 天然是能夠實現 html to pdf 的了. jsreport 後來的版本也切換到 chromium 了.
chromium 的底層接口不太友好,google 本身又推出了 Puppeteer
這是 js 的庫, 能夠用 nodejs 運行, 它是 chromium 友好的 api. 幾行代碼就能夠實現各作遊覽器行爲. 固然就包括了 html to pdf。
.net core 和 nodejs 是好朋友, 要在 core 調用 nodejs 的 package 是很簡單的.
因此又有了 https://github.com/kblok/puppeteer-sharp
我就是使用 asp.net core node service + Puppeteer 來實現 html to pdf 的.
上面說的都是 server side 的作法
client side 也有一個作法.
用的是一個叫 jsPDF 的插件, 它是作 pdf 的插件, 而不是 html to pdf
實現手法是經過打開 browser 使用 url data:application/pdf 的方式輸出 base64
遊覽器支持這種 data 直接打開 file 的功能,不僅 pdf, image 也是能夠這樣打開.
若是要 html to pdf 能夠配上一個 html to canvas 的插件
把 html 轉成 canvas 而後變成圖片在輸入進 pdf ( 固然這樣文字就變成不能夠 search 了 )
目前這個方案問題仍是比較多的, 好比 htmltocanvas 依然處於不穩定版本,並且維護也很弱.
記入一下 Puppeteer 的一些東西
module.exports = function (callback, bodyTemplate, format, headerTemplate, footerTemplate) { const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); //await page.goto('https://www.stooges.com.my'); //await page.screenshot({ path: 'example.png' }); //await page.goto('https://www.stooges.com.my', { waitUntil: 'networkidle2' }); await page.setContent(htmlTemplate, { waitUntil: 'networkidle2' }); await page.pdf({ //printBackground: true, path: 'demo.pdf', format: format, //format or follow width 792 & height 1121, got footer gap... bug landscape: (format === 'A5') ? true : false, margin: { top: 125, right: 16, bottom: 60, left: 32 },// 左右會縮小比例, 上下不會; 若是寬沒有hit 到paper的寬就不會縮小; top bottom min 21 for display element displayHeaderFooter: true, headerTemplate: headerTemplate, //header no backgroung color footerTemplate: footerTemplate }); await browser.close(); //console.log(process.version); // check node version callback(null); })(); }
流程大概就是開一個 browser, 開一個 page, 而後訪問你要的網址或者直接寫入 html
而後調用 pdf 方法.
當你設定 format A4 時, 你的 width height 就自動設定了, 因該是 792x1121px
header 的開始時 margin 21, 也就是說若是你的 header 內容時 50, 那麼你得要 set margin 71 才能夠哦
還有一個比較嚴重的是 header 和 footer 的字體和 width 都會比 body 大
好比一個 div 在 header 和 body 都 set 500px, 可是出來的效果就是 header 比較大
大多少呢? 大概是 4/3 . github 還有 issues 討論這個鬼呢.
最後說一說 node_modules 的事兒.
我也是作前端的, 開發前端多少會接觸 node_modules
這裏說說,前端和後端的小區別.
1. 後端不須要打包
前端依賴的庫也是存放在 node_modules 裏的, 而後經過 webpack 去打包出來
作後端就不須要去打包了,打包的目的是爲了減小下載, 在服務端 require 另外一個模塊是很快的.
2. node_modules 須要在 server side
前端打包之後,咱們的服務器是不須要存在 node_modules 這個文檔的,可是若是是 nodejs 就須要
咱們對 node_modules 的印象是...很大很大,不少文件.
但其實不少是 dev 才須要的, 因此在 server side npm install 的時候記得寫上 --production
只使用 puppeteer 的話,大概 3x 個 files 而已.
3. puppeteer 自帶的 chromium
安裝 puppeteer 時它會去安裝一個 chromium, 放在 node module 裏.
若是有多個項目,你能夠作一個 share 的 chromium
調用的時候放一個 path 就能夠了, 咱們能夠經過 Environment Variables 來設置這些 (好比要不要 skip download chromium 和 chromium 的路徑等等...)
const browser = await puppeteer.launch({ executablePath: 'C:/keatkeat/my projects/asp.net core/2.2/html-to-pdf/Project/.local-chromium/win64-669486/chrome-win/chrome.exe' });
參考資源 :
https://www.nrecosite.com/pdf_generator_net.aspx#
https://github.com/wkhtmltopdf/wkhtmltopdf
https://github.com/rdvojmoc/DinkToPdf
https://github.com/jsreport/jsreport
https://github.com/jsreport/jsreport-dotnet
https://github.com/kblok/puppeteer-sharp
https://www.paperplane.app/pricing
https://juejin.im/entry/5ac1e7c05188257ddb0fc853
https://blog.risingstack.com/pdf-from-html-node-js-puppeteer/
https://zhaoqize.github.io/puppeteer-api-zh_CN/#/
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions
http://www.rotisedapsales.com/snr/cloud2/website/jsPDF-master/docs/jsPDF.html
https://github.com/niklasvh/html2canvas
https://dev.to/damcosset/generate-a-pdf-from-html-and-back-on-the-front-5ff5
https://developers.google.com/web/updates/2017/04/headless-chrome
https://zhuanlan.zhihu.com/p/33015883
https://www.jianshu.com/p/db1b230e3415
https://stackoverflow.com/questions/564650/convert-html-to-pdf-in-net
https://www.quora.com/What-is-the-best-HTML-to-PDF-converter-tool
https://www.reddit.com/r/dotnet/comments/7i6ljt/what_is_currently_the_best_way_to_convert_html_to/