教你兩招,輕松搞定Html頁面導出為Pdf文件
本文轉載自微信公眾號「愛寫Bug的麥洛」,作者麥洛。轉載本文請聯系愛寫Bug的麥洛公眾號。
Hi,大家好,我是麥洛,最近項目中遇到了將html頁面導出為pdf文件,現在將相關內容分享出來,希望幫到有需要的伙伴
需求場景
在招投標軟件中,每個標段結束評標之后,都會生成評標報告
評標報告主要包含項目信息,標段信息,投標人信息,投標人報價,評標專家打分等情況,相對來說信息量還是比較大,假如我們要導出評標報告該如何做?
- html頁面直接導出為pdf
- 后端組裝頁面,導出pdf
對比兩種方式,很明顯第一種方式優越性更好。即方便實現,又避免了由于頁面的變動而需要改動導出功能代碼的尷尬
方案調研
查閱了一些資料,目前市面上流行的解決方案主要有以下幾種
- wkhtmltopdf
- iText
- html2canvas+jsPDF
其中前面兩種為后端實現方式,第三種為純前端實現方式;
首先讓我們來看一下wkhtmltopdf
從github上可以看出,wkhtmltopdf的Star數量總共有11.1K,由此可見他的火爆程度。經過測驗以后,我發現他的效果也是最好的。但是由于我們的項目采用了vue,貌似它不支持vue語法。所以我這邊最后只能退而求其次,使用了其他技術來實現。
接著我們來看一下html2canvas+jsPDF的方式
這種方式是采用以上兩個開源項目來實現。網上把它稱作是一種曲線救國的方式。首先我們利用html2canvas將HTML網頁保存成canvas圖片,然后我們在利用jsPDF將canvas圖片生成PDF文件。所以最終我們拿到的PDF文件并不是真正意義上的PDF文件,而是一張圖片。這也導致我們無法編輯PDF文件。而且質量也一般。
最后我們來看一看iText
itext7好像是最新版本,這種方式適合于維護PDF模板然后動態添加內容,有需要的小伙伴可以了解一下。
由于我們的項目前端是采用vue,經過測試以后,我發現wkhtmltopdf好像并不支持Vue語法。也可能是我的使用方式不當。歡迎小伙伴指正。而且itext7更多用于需要去維護PDF模板的場景,并不適合我本次的需求。所以我最終使用html2canvas+jsPDF的方式來實現。
實戰案例
html2canvas+jsPDF
現在,我們來看看html2canvas+jsPDF的實現方式
首先需要引入html2canvas和jsPDF的依賴文件。大家可以從官網下載。我也會在文末的資源包中放一份,方便大家使用。
- //導出pdf文件[html2canvas&&jspdf結合方式]
- getPdf: function () {
- var that = this;
- //影藏不需要的按鈕
- that.buttonShow = !that.buttonShow;
- //不寫會報錯
- window.jsPDF = window.jspdf.jsPDF;
- //將body的內容保存為一個圖片
- var html2canvas1 = html2canvas(document.body, {
- //圖片跨域加載
- useCORS: true,
- onrendered: function (canvas) {
- var contentWidth = canvas.width
- var contentHeight = canvas.height
- //一頁pdf顯示html頁面生成的canvas高度
- var pageHeight = contentWidth / 592.28 * 841.89
- //未生成pdf的html頁面高度
- var leftHeight = contentHeight
- //頁面偏移
- var position = 0
- //a4紙的尺寸[595.28,841.89] html頁面生成的canvas在pdf的寬高
- var imgWidth = 595.28
- var imgHeight = 592.28 / contentWidth * contentHeight
- //獲取圖片的base64數據
- var pageData = canvas.toDataURL('image/jpeg', 1.0)
- //document.body.appendChild(canvas);
- var PDF = new jsPDF('', 'pt', 'a4');
- if (leftHeight < pageHeight) {
- PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
- } else {
- //分頁
- while (leftHeight > 0) {
- PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
- leftHeight -= pageHeight
- position -= 841.89
- if (leftHeight > 0) {
- PDF.addPage()
- }
- }
- }
- //下載pdf
- var save = PDF.save(that.sectionInfo.sectionName+"評標報告" + '.pdf');
- //將pdf文件轉為blob對象
- var blob = save.output("blob");
- //保存pdf文件到服務器
- that.savePdf(blob)
- },
- });
- },
由于這種方式是純前端實現。如果我們想要把PDF保存一份到服務器,需要自己手動實現將文件上傳到服務器。
wkhtmltopdf
接下來我們來看看wkhtmltopdf這種方式如何實現?
如果我們要使用wkhtmltopdf,需要安裝官方提供的軟件,大家可以在他的官網進行下載。
https://wkhtmltopdf.org/downloads.html
安裝完成以后我們需要將安裝路徑配置的我們的工具類中。
- public class WKHtmlToPdfUtil {
- private static final String WINDOWS_URL = "D:/wkhtmltopdf/bin/wkhtmltopdf.exe";
- private static final String LINUX_URL = "/opt/wkhtmltox/bin/wkhtmltopdf";
下面我們看一看如何使用,我們需要將我們導出的頁面的路徑拼接后作為參數傳遞進來。
- String serverUrl = request.getScheme() + "://" + request.getServerName()+":"+request.getServerPort();
- //組裝需要導出頁面的地址
- serverUrl += request.getContextPath()+"/";
- serverUrl += "evaluate/report/evaluateSectionReport?projectId="+projectId+"§ionId="+sectionId;
- logger.info(serverUrl);
- // 工具類調用
- exportPdf(serverUrl,response);
- /**
- * @Title: 導出pdf到服務器
- * @param
- * @return
- */
- public static void exportPdf(String serverUrl, HttpServletResponse response){
- try {
- ArrayList<String> urlList = new ArrayList<>();
- urlList.add(serverUrl);
- String folder = Global.getProfile() + "resultReports/";
- // 判斷此路徑所有目錄是否存在,不存在則創建
- File file = new File(folder);
- if(!file.exists() && !file.isDirectory()){
- // mkdir()創建此抽象路徑名指定的目錄。如果父目錄不存在則創建不成功
- // mkdirs()創建此抽象路徑名指定的目錄,包括所有必需但不存在的父目錄
- file.mkdirs();
- }
- // 生成隨機的附件路徑(時間戳+4位隨機數)
- Random random = new Random();
- String fileName = "milolee"+random.nextInt(10);
- //資源包中,自己下載
- WKHtmlToPdfUtil.htmlToPdf(urlList, folder+fileName+".pdf");
- //資源包中,自己下載
- // 生成成交通知書pdf文件到服務器之后下載到客戶端
- FileUtils.downLoadFile(folder,fileName+".pdf",response);
- } catch (Exception e){
- e.printStackTrace();
- }
- }
工具類WKHtmlToPdfUtil和FileUtils我放到資源包中,大家自行下載,太多了就不一一粘貼了
接下來我們看一看導出我的CSDN首頁的效果,還是很棒的
小結
本文主要介紹了如何將html頁面導出為pdf文件,希望給遇到類似需求的小伙伴一點思路,沒遇到的也可以收藏一下,以后說不定用得到。
由于本文設計到的代碼比較多,我會打包上傳到csdn,大家可以自行下載