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
































