在網(wǎng)頁(yè)開(kāi)發(fā)中,實(shí)現(xiàn) PDF 下載功能是常見(jiàn)的需求。以下是幾種主流實(shí)現(xiàn)方式及其詳細(xì)代碼示例:
方案一,使用瀏覽器原生API(window.print)
<!DOCTYPE html><html><head> <title>打印為PDF</title> <style> @media print { .no-print { display: none; } body { margin: 0; padding: 10mm; } } </style></head><body> <div id="printable-content"> <h1>可打印內(nèi)容</h1> <p>使用瀏覽器打印功能保存為PDF</p> </div>
<button class="no-print" onclick="window.print()">打印/保存為PDF</button></body></html>
這個(gè)插件僅僅是喚起打印的功能,讓用戶另存為 pdf 不合適
方案二,使用純前端方案(jsPDF + html2canvas)
<!DOCTYPE html><html><head> <title>HTML轉(zhuǎn)PDF</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> <style> #content { width: 800px; padding: 20px; background: #f5f5f5; } </style></head><body> <div id="content"> <h1>這是要導(dǎo)出為PDF的內(nèi)容</h1> <p>使用jsPDF和html2canvas庫(kù)可以輕松實(shí)現(xiàn)HTML轉(zhuǎn)PDF功能</p> <table border="1"> <tr><th>姓名</th><th>年齡</th></tr> <tr><td>張三</td><td>25</td></tr> </table> </div>
<button onclick="generatePDF()">下載PDF</button>
<script> function generatePDF() { const { jsPDF } = window.jspdf; const element = document.getElementById('content');
html2canvas(element).then(canvas => { const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF('p', 'mm', 'a4'); const imgProps = pdf.getImageProperties(imgData); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight); pdf.save('document.pdf'); }); } </script></body></html>
看著幾乎完美,這個(gè)技術(shù)棧,最核心的就是:必須要用到 dom 元素渲染,試想一下,你做了一個(gè)導(dǎo)出功能,總不能讓客戶必須先打開(kāi)頁(yè)面等 html 渲染完后,再導(dǎo)出吧?或者display:none,打印出來(lái)一個(gè)空白。
此路不通,就只能重新尋找新的方向
方案三,html2pdf
npm install html2pdf.js
<template> <div class="container"> <button @click="generatePDF">下載PDF</button> </div></template><script setup>import html2pdf from 'html2pdf.js'let element = ` <h1>前端人</h1> <p>學(xué)好前端,走遍天下都不怕</p> ...`;function generatePDF() { const opt = { margin: 10, filename: 'hello_world.pdf', image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2 }, jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' } }; html2pdf().from(element).set(opt).save();}</script>
功能正常,似乎一切都完美
問(wèn)題沒(méi)有想的那么簡(jiǎn)單如果我們的html是純文本元素,這程序跑起來(lái)沒(méi)有任何問(wèn)題,但我們抓取的信息都源于互聯(lián)網(wǎng),html結(jié)構(gòu)怎么可能會(huì)這么簡(jiǎn)單?如果我們的html中包含圖片信息 ,此時(shí)你會(huì)發(fā)現(xiàn),導(dǎo)出來(lái)的 pdf,圖片占位處是個(gè)空白塊
那我理解的圖片同步加載是什么意思呢?簡(jiǎn)單來(lái)說(shuō),就是將圖片轉(zhuǎn)成Base64,因?yàn)檫@種方式,即使說(shuō)無(wú)網(wǎng)的情況也能正常加載圖片,因此我憑感覺(jué)斷定,這就是圖片同步加載
基于這個(gè)思路,我寫了個(gè)完整 demo
<template> <div class="container"> <button @click="generatePDF">下載PDF</button> </div></template><script setup>import html2pdf from 'html2pdf.js'async function convertImagesToBase64(htmlString) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = htmlString; const images = tempDiv.querySelectorAll('img'); for (const img of images) { try { const base64 = await getBase64FromUrl(img.src); img.src = base64; } catch (error) { console.error(`無(wú)法轉(zhuǎn)換圖片 ${img.src}:`, error); } } return tempDiv.innerHTML;}function getBase64FromUrl(url) { return new Promise((resolve, reject) => { const img = new Image(); img.crossOrigin = 'Anonymous'; img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const dataURL = canvas.toDataURL('image/png'); resolve(dataURL); }; img.onerror = () => { reject(new Error('圖片加載失敗')); }; img.src = url; });}let element = ` <div> <img src='http://t13.baidu.com/it/u=2041049195,1001882902&fm=224&app=112&f=JPEG?w=500&h=500' style="width: 300px;" /> <p>職業(yè):前端</p> <p>技能:唱、跳、rap</p> </div>`;function generatePDF() { element =`<style> img { max-width: 100%; max-height: 100%; vertical-align: middle; height: auto !important; width: auto !important; margin: 10px 0; } </style>` + element; convertImagesToBase64(element) .then(convertedHtml => { const opt = { margin: 10, filename: '前端大法好.pdf', image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2 }, jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' } }; html2pdf().from(convertedHtml).set(opt).save(); }) .catch(error => { console.error('轉(zhuǎn)換過(guò)程中出錯(cuò):', error); });}</script>
此時(shí)就大功告成啦!不過(guò)得提一句:圖片的 URL 鏈接必須是同源或者允許跨越的,否則就會(huì)存在圖片加載異常的問(wèn)題。
閱讀原文:原文鏈接
該文章在 2025/7/3 14:24:50 編輯過(guò)