純前端生成PDF(jsPDF)并下載保存或上傳到OSS的代碼示例
前言
在工作中遇到了一個(gè)需求,就是把前端頁面生成PDF并保存在本地,因?yàn)榍岸司W(wǎng)站可能會(huì)展示各種表格,圖表信息內(nèi)容并帶有比較鮮艷的色彩樣式,如果讓后端生產(chǎn)的PDF的話樣式可能和前端頁面展示的有所差異,所以這個(gè)任務(wù)就落到了前端的身上。
技術(shù)涉及
- jsPDF
html2canvas
ali-oss
代碼實(shí)現(xiàn)
1、獲取DOM結(jié)點(diǎn)
首先需要獲取需要打印的DOM結(jié)點(diǎn),這個(gè)時(shí)候獲取的DOM結(jié)點(diǎn)是帶有樣式的,就相當(dāng)于頁面中的內(nèi)容
const eleHtml = document.querySelector('.zxksBody');
2、獲取打印容器的屬性
首先做個(gè)兼容判斷,判斷是否取到了DOM結(jié)點(diǎn)信息,如果取到了DOM結(jié)點(diǎn)就獲取DOM結(jié)點(diǎn)的內(nèi)容,進(jìn)行高度和寬度的賦值
if (eleHtml) { let eleW = eleHtml.offsetWidth; // 獲得該容器的寬 let eleH = eleHtml.offsetHeight; // 獲得該容器的高 }
3、生成PDF
這一步就是把獲取到的DOM結(jié)點(diǎn),通過jsPDF和html2canvas 生成為PDF
html2canvas(eleHtml, { dpi: 300, width: eleW, height: eleH, scale: 2, // 提高渲染質(zhì)量 useCORS: true //允許canvas畫布內(nèi) 可以跨域請(qǐng)求外部鏈接圖片, 允許跨域請(qǐng)求。 }).then(async (canvas) => { const pdf = new jsPDF('', 'pt', 'a4'); const imgData = canvas.toDataURL('image/png', 1.0); //a4紙的尺寸[595.28,841.89],html頁面生成的canvas在pdf中圖片的寬高 const imgWidth = 555.28; //一頁pdf顯示html頁面生成的canvas高度; const imgHeight = 555.28 / canvas.width * canvas.height; // 計(jì)算分頁 const pageHeight = 841.89; //未生成pdf的html頁面高度 let leftHeight = imgHeight; //頁面偏移 let position = 0; if (leftHeight < pageHeight) { //在pdf.addImage(pageData, 'JPEG', 左,上,寬度,高度)設(shè)置在pdf中顯示 pdf.addImage(imgData, 'PNG', 20, 20, imgWidth, imgHeight); } else { // 分頁 while (leftHeight > 0) { pdf.addImage(imgData, 'PNG', 20, position, imgWidth, imgHeight); leftHeight -= pageHeight; position -= 841.89; if (leftHeight > 0) { pdf.addPage(); } } });
4、保存本地或者上傳OSS
保存本地
保存本地的話比較簡(jiǎn)單,直接調(diào)用PDF庫自帶的方法就可以保存到本地
pdf.save(`${state.xsMc}-${state.xsBh}.pdf`)
上傳OSS
上傳的OSS的話就比較復(fù)雜一點(diǎn),首先就是需要配置OSS的內(nèi)容,然后把PDF轉(zhuǎn)換為Blob對(duì)象,最后就是調(diào)用OSS的接口實(shí)現(xiàn)上傳。
// 配置OSS const client = new OSS({ region: "******", bucket: 'bucketName', endpoint: 'endpoint', stsToken: 'securityToken', accessKeyId: 'accessKeyId', accessKeySecret: 'accessKeySecret', }); // 將 PDF 文件轉(zhuǎn)換為 Blob 對(duì)象 const pdfBlob = pdf.output('blob'); // 調(diào)用OSS上方實(shí)現(xiàn)上傳 const fileRes = await client.put(`${state.xsMc}-${state.xsBh}.pdf`, pdfBlob); console.log(fileRes, '接收返回的OSS信息');
5、注意事項(xiàng)
- 使用html2canvas和jsPDF可能會(huì)遇見文本錯(cuò)位或者樣式錯(cuò)誤問題,這個(gè)時(shí)候需要進(jìn)行調(diào)整,可以通過html2canvas中的onclone回調(diào)方法進(jìn)行調(diào)整
html2canvas(eleHtml, { onclone: (documentClone) => { // 在克隆的文檔上進(jìn)行修改 const partRight2 = documentClone.querySelector('.partRight2'); const titleBars = documentClone.querySelectorAll('.titleBar'); if (partRight2) { partRight2.style.display = 'none'; // 隱藏內(nèi)容 } if (titleBars) { //修改樣式屬性 titleBars.forEach(titleBar => { titleBar.style.marginTop = '-8px'; titleBar.style.marginBottom = '20px'; }); } }, dpi: 300, width: eleW, height: eleH, scale: 2, // 提高渲染質(zhì)量 useCORS: true //允許canvas畫布內(nèi) 可以跨域請(qǐng)求外部鏈接圖片, 允許跨域請(qǐng)求。 }).then(async (canvas) => { ....... });
- 對(duì)于在獲取DOM時(shí),帶有滾動(dòng)條的內(nèi)容無法正確獲取他的高度和寬度,內(nèi)容可能會(huì)被遮蓋無法正確打印,這個(gè)時(shí)候需要在打印前更改頁面中的DOM樣式才能正確打印
// 獲取全部?jī)?nèi)容 const eleHtml = document.querySelector('.zxksBody'); // 在生成canvas之前就把樣式進(jìn)行更改,獲取盒子的正常高度或者寬度,防止樣式被遮蓋, const changeHeight = document.querySelector('.zxksContent'); if (changeHeight) { changeHeight.style.height = '100%'; // 更改高度 } html2canvas(eleHtml, { dpi: 300, width: eleW, height: eleH, scale: 2, // 提高渲染質(zhì)量 useCORS: true //允許canvas畫布內(nèi) }).then(async (canvas) => { ..... // 在打印完成后,再把樣式改回去 if (changeHeight) { changeHeight.style.height = 'calc(100vh - 182px)'; } }
- 對(duì)于帶有滾動(dòng)條的div盒子,在點(diǎn)擊打印時(shí),最好把頁面內(nèi)容進(jìn)行更改,防止無法正確獲取盒子高度,導(dǎo)致文字被隱藏,在打印完成后,在更改回去
// 對(duì)于vue
可以使用v-if進(jìn)行更換,把展示的內(nèi)容保存在div中,去掉溢出滾動(dòng)功能
// 對(duì)于react
可以使用三元運(yùn)算符進(jìn)行判斷,展示的內(nèi)容
6、完整代碼
const printPdf = async () => { const client = new OSS({ const client = new OSS({ region: "******", bucket: 'bucketName', endpoint: 'endpoint', stsToken: 'securityToken', accessKeyId: 'accessKeyId', accessKeySecret: 'accessKeySecret', }); try { // 獲取全部?jī)?nèi)容 const eleHtml = document.querySelector('.zxksBody'); // 帶有移除隱藏的功能 const changeHeight = document.querySelector('.zxksContent'); if (changeHeight) { changeHeight.style.height = '100%'; // 更改高度 } if (eleHtml) { let eleW = eleHtml.offsetWidth; // 獲得該容器的寬 let eleH = eleHtml.offsetHeight; // 獲得該容器的高 // 確保獲取加載完全的DOM setTimeout(() => { html2canvas(eleHtml, { onclone: (documentClone) => { // 在克隆的文檔上進(jìn)行修改 const partRight2 = documentClone.querySelector('.partRight2'); const titleBars = documentClone.querySelectorAll('.titleBar'); if (partRight2) { partRight2.style.display = 'none'; // 隱藏內(nèi)容 } if (titleBars) { titleBars.forEach(titleBar => { titleBar.style.marginTop = '-8px'; titleBar.style.marginBottom = '20px'; }); } }, dpi: 300, width: eleW, height: eleH, scale: 2, // 提高渲染質(zhì)量 useCORS: true //允許canvas畫布內(nèi) 可以跨域請(qǐng)求外部鏈接圖片, 允許跨域請(qǐng)求。 }).then(async (canvas) => { const pdf = new jsPDF('', 'pt', 'a4'); const imgData = canvas.toDataURL('image/png', 1.0); const imgWidth = 555.28; const imgHeight = 555.28 / canvas.width * canvas.height; // 計(jì)算分頁 const pageHeight = 841.89; let leftHeight = imgHeight; let position = 0; if (leftHeight < pageHeight) { pdf.addImage(imgData, 'PNG', 20, 20, imgWidth, imgHeight); } else { while (leftHeight > 0) { pdf.addImage(imgData, 'PNG', 20, position, imgWidth, imgHeight); leftHeight -= pageHeight; position -= 841.89; if (leftHeight > 0) { pdf.addPage(); } } } // 將 PDF 文件轉(zhuǎn)換為 Blob 對(duì)象 const pdfBlob = pdf.output('blob'); // 使用 OSS 客戶端上傳 Blob 對(duì)象 try { const fileRes = await client.put(`${state.xsMc}-${statexsBh}.pdf`, pdfBlob); console.log('client res', fileRes); } catch (err) { console.error('PDF上傳失敗,請(qǐng)重新提交!', err); } if (changeHeight) { changeHeight.style.height = 'calc(100vh - 182px)'; } }); }, 1000); } } catch (error) { console.log("Error!", error); if (changeHeight) { changeHeight.style.height = 'calc(100vh - 182px)'; } } };
總結(jié)
到此這篇關(guān)于純前端生成PDF(jsPDF)并下載保存或上傳到OSS的文章就介紹到這了,更多相關(guān) 前端生成PDF并下載保存或上傳到OSS內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript實(shí)現(xiàn)手動(dòng)點(diǎn)贊效果
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)手動(dòng)點(diǎn)贊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04通過高德地圖API獲得某條道路上的所有坐標(biāo)用于描繪道路的方法
這篇文章主要介紹了通過高德地圖API獲得某條道路上的所有坐標(biāo)用于描繪道路的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08對(duì)象特征檢測(cè)法判斷瀏覽器對(duì)javascript對(duì)象的支持
就是將需要檢測(cè)的方法/對(duì)象作為if語句的判斷條件,具體做法如下2009-07-07JavaScript的DOM與BOM的區(qū)別與用法詳解
這篇文章主要為大家詳細(xì)介紹了JavaScript的DOM與BOM的區(qū)別與用法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03js學(xué)習(xí)總結(jié)之DOM2兼容處理this問題的解決方法
這篇文章主要為大家詳細(xì)介紹了js學(xué)習(xí)總結(jié)之DOM2兼容處理this問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07基于JavaScript代碼實(shí)現(xiàn)兼容各瀏覽器的設(shè)為首頁和加入收藏
但是由于瀏覽器的兼容性問題,之前用的很多代碼都失去效果,下面就給出一段能夠兼容各個(gè)瀏覽器的代碼,也不能夠算是兼容,只能說在不支持的瀏覽器中能夠給出提示,對(duì)js兼容各個(gè)瀏覽器設(shè)為首頁加入收藏相關(guān)知識(shí)感興趣的朋友可以參考下本文2016-01-01JavaScript代碼實(shí)現(xiàn)禁止右鍵、禁選擇、禁粘貼、禁shift、禁ctrl、禁alt
這篇文章主要介紹了JavaScript代碼實(shí)現(xiàn)禁止右鍵、禁選擇、禁粘貼、禁shift、禁ctrl、禁alt,需要的朋友可以參考下2015-11-11