摘要:而打印所用的頁面需要用到用戶信息,所以我們登錄了一個超管帳號來執行打印操作。在訪問頁面的時候通過參數校驗判斷是否是打印而打開的頁面,如果是則登錄超管帳號。
現狀
聲享是一個基于 ThinkJS 開發的在線制作 PPT 平臺。聲享制作的 PPT 支持代碼高亮、圖片上傳、神奇效果等功能,同時你可以在聲享收藏自己喜歡的 PPT 、對自己的 PPT 進行分類管理。其中有一個 PDF 導出的功能,可以將自己制作的 PPT 導出成 PDF 保存到本地。
功能實現比較簡單,只是提供了一個頁面,用戶需要手動去打印成 PDF。這個方案存在一些問題:
由于使用了 iframe 懶加載導致未加載的 iframe 無法正常顯示。
該種方案只能打印所有頁面的初始狀態。如果頁面中存在切換動畫,可能會丟失部分 PPT 信息。
需要用戶手動操作,提高了使用難度。
如果是前端來生成 PDF,這些問題基本可以得到解決,但是開發量比較大而且存在一個效率問題。如果 PPT 頁面存在多個 iframe,PDF 的生成時間過長會讓用戶長時間等待,明顯不太合適。最終還是決定服務端來生成 PDF,才有了后來 Puppeteer 的嘗試。
Puppeteer什么是Puppeteer呢?官方給的解釋是:
Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.
簡而言之,這貨是一個提供高級 API 的 node 庫,能夠通過 devtool 控制 headless 模式的 Chrome 或者 Chromium,它可以在 headless 模式下模擬任何的人為操作。通過它我們可以實現:
生成頁面的截圖或者 PDF。
抓取 SPA(單頁應用)并生成預渲染內容(即“SSR”(服務器端渲染))。
自動提交表單,進行 UI 測試,鍵盤輸入等。
...
通過 Puppeteer,我們可以直接使用 Chrome 把我們需要的內容導出為 PDF。對比以前的實現方式有以下優點:
不需要用戶手動操作,服務端生成 PDF 后直接以郵件的方式發送給用戶。
PPT 中的動畫可以模擬用戶翻頁的動作觸發,然后以初始、結束兩張 PDF 的方式展示,不會丟失 PPT 內容。
不需要考慮圖片/ iframe 跨域等問題。
可以說 Puppeteer 完美的解決來我們一期 PDF 導出存在的問題。
解決方案我們基本的實現思路是:
打開一個正常的 PPT 播放頁,獲取需要打印的 DOM 元素并翻頁 。
重復第一步操作直至到最后一頁 。
清空頁面內容并將前兩步獲得的頁面內容依次填充到當前頁面(為什么要依次填充會在后面解釋)。
對應上述方案實現的部分代碼如下:
通過 Puppeteer 打開指定的頁面。
// 測試時建議headless設置為false,以便可以直觀看到頁面效果 this.browser = await puppeteer.launch({headless: this.isDebug}); this.page = await this.browser.newPage(); await this.page.goto("https://xxxxx.com", { waitUntil:"networkidle2" });
打開頁面后可以通過 Puppeteer 模擬用戶翻頁操作,每次翻頁后緩存需要打印的 DOM 元素字符串。
let canNext; let i = 0; const content = {}; do { canNext = await this.page.$(".navigate-right.enabled"); const iframes = await this.page.$$(".PluginPage.present iframe").length; content[i++] = { iframe: iframes, domStr: await this.page.$eval(".RevealViewPort", el => el.outerHTML) } if (canNext) { await this.page.click(".navigate-right"); // 等待翻頁動畫 await this.page.waitFor(1000); } } while (canNext);
獲取到要打印的所有頁面 DOM 后,替換掉原來的頁面內容。因為 $evaluate 方法中不支持調用外部變量所以只能以傳參的方式使用。
this.page.evaluate(domStr => document.body.innerHTML = domStr, content);
調用生成 PDF 的 API。
this.page.pdf({ path: path.join(think.ROOT_PATH, "runtime/xxx.pdf"), format: "A4", landscape: true, printBackground: true //如果要顯示背景,此屬性要設置為true })
使用 nodemailer 發送郵件給用戶。這一步如果想使用本地的 SMTP 服務請用 nodemailer 的 2.7.5 的版本,此版本后這項功能被刪除了。
let transporter = nodemailer.createTransport({ host: "smtp.ym.163.com", port: 994, secure: true, auth: { user: "xxx@xxx.com", pass: "xxx" } }); transporter.sendMail({ from: "xxx@xxx.com", to: "xxx@xxx.com",, subject: "【聲享】xxx", attachments: [{ filename: "xxx.pdf", path: path.join(think.ROOT_PATH, "runtime/xxx.pdf"), contentType: "application/pdf" }] })開發中需要注意的問題
用戶登錄
使用 Puppeteer 打開頁面相當于你新啟動了一個瀏覽器實例,頁面中的 seession 和 cookie 是空的。而打印所用的頁面需要用到用戶信息,所以我們登錄了一個超管帳號來執行打印操作。在 ThinkJS 中可以通過中間件來實現這項功能。在訪問頁面的時候通過參數校驗判斷是否是打印而打開的頁面,如果是則登錄超管帳號。
// 打開指定頁面時通過校驗后面參數判斷是否以超管登錄 module.exports = options => { return async (ctx, next) => { const { token, ctime } = ctx.query; const md5Str = tockenGenerator(); if (md5Str === token) { await ctx.session("userInfo", adminUser); } return next(); }; };
Puppeteer 啟動
如果服務端是運行在 root 權限下,在啟動 Puppeteer 時要添加 --no-sandbox 參數,否則 Chrome/Chromium 會啟動失敗。詳情見 Running as root without — no-sandbox is not supported。這個權限問題在linux以root用戶使用 Chrome 的時候同樣適用。
this.browser = await puppeteer.launch({args:["--no-sandbox"]});
iframe 無法加載
聲享支持頁面內嵌入 iframe,在打印的時候碰到一個問題。如果同時在頁面上插入 iframe 過多,后面的 iframe 會直接卡住不再加載。所以 iframe 最好分批插入或者一個一個插入,同時設定10秒來加載iframe。 如果想精確控制 iframe 也可以使用 API 等待 iframe 完全加載再執行后續操作。
for (let i = 0; i < pages.length; i++) { const page = pages[i]; await this.page.$evaluate(content => { const divDom = document.createElement("div"); divDom.innerHTML = content; document.body.appendChild(divDom.childNodes[0]) }, page.domStr); if (page.iframe) await this.page.waitFor(10000 * page.iframe); }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102645.html
摘要:一背景需求因為數據包含機密信息,所以得自己搭建圖表導出服務器在后臺生成對應圖表以圖片的形式導出保存。圖表個性化程度較高,如一些圖列是沒有的,但在前端可以利用實現。每周定時執行上述生成圖表的任務,保存到指定位置。 一、背景需求 1、因為數據包含機密信息,所以得自己搭建圖表導出服務器;在后臺生成對應Highcharts圖表、以圖片的形式導出保存。2、圖表個性化程度較高,如一些圖列是High...
摘要:可以通過的提供的直接控制模擬大部分用戶操作來進行或者作為爬蟲訪問頁面來收集數據。 ??骨架屏是在頁面數據尚未加載完成前先給用戶展示出頁面的大致結構,直到請求數據返回后再顯示真正的頁面內容;隨著單頁應用( SPA )的越來越流行,單頁應用的用戶體驗也越來越得到前端開發者的關注;為了優化用戶體驗,在數據到達用戶之前,往往會在頁面上加上 loading 的效果,而現在,越來越多的場景傾向于使...
摘要:本文將介紹如何使用和抓取主流的技術博客文章,然后用搭建一個小型的技術文章聚合平臺。是谷歌開源的基于和的自動化測試工具,可以很方便的讓程序模擬用戶的操作,對瀏覽器進行程序化控制。相對于,是新的開源項目,而且是谷歌開發,可以使用很多新的特性。 背景 說到爬蟲,大多數程序員想到的是scrapy這樣受人歡迎的框架。scrapy的確不錯,而且有很強大的生態圈,有gerapy等優秀的可視化界面。但...
摘要:本文將介紹如何使用和抓取主流的技術博客文章,然后用搭建一個小型的技術文章聚合平臺。是谷歌開源的基于和的自動化測試工具,可以很方便的讓程序模擬用戶的操作,對瀏覽器進行程序化控制。相對于,是新的開源項目,而且是谷歌開發,可以使用很多新的特性。 背景 說到爬蟲,大多數程序員想到的是scrapy這樣受人歡迎的框架。scrapy的確不錯,而且有很強大的生態圈,有gerapy等優秀的可視化界面。但...
摘要:獲取獲取上下文句柄執行計算銷毀句柄除此之外,還可以使用意為在瀏覽器環境執行腳本,可傳入第二個參數作為句柄,而則針對選中的一個元素執行操作。 我們日常使用瀏覽器或者說是有頭瀏覽器時的步驟為:啟動瀏覽器、打開一個網頁、進行交互。 無頭瀏覽器指的是我們使用腳本來執行以上過程的瀏覽器,能模擬真實的瀏覽器使用場景。 有了無頭瀏覽器,我們就能做包括但不限于以下事情: 對網頁進行截圖保存為圖片或 ...
閱讀 2230·2021-09-24 10:31
閱讀 3880·2021-09-22 15:16
閱讀 3400·2021-09-22 10:02
閱讀 1016·2021-09-22 10:02
閱讀 1832·2021-09-08 09:36
閱讀 1978·2019-08-30 14:18
閱讀 612·2019-08-30 10:51
閱讀 1870·2019-08-29 11:08