在網(wǎng)頁開發(fā)中,實現(xiàn) PDF 下載功能是常見的需求。以下是幾種主流實現(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>
這個插件僅僅是喚起打印的功能,讓用戶另存為 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庫可以輕松實現(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>
看著幾乎完美,這個技術(shù)棧,最核心的就是:必須要用到 dom 元素渲染,試想一下,你做了一個導(dǎo)出功能,總不能讓客戶必須先打開頁面等 html 渲染完后,再導(dǎo)出吧?或者display:none,打印出來一個空白。
此路不通,就只能重新尋找新的方向
方案三,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>
功能正常,似乎一切都完美
問題沒有想的那么簡單如果我們的html是純文本元素,這程序跑起來沒有任何問題,但我們抓取的信息都源于互聯(lián)網(wǎng),html結(jié)構(gòu)怎么可能會這么簡單?如果我們的html中包含圖片信息 ,此時你會發(fā)現(xiàn),導(dǎo)出來的 pdf,圖片占位處是個空白塊
那我理解的圖片同步加載是什么意思呢?簡單來說,就是將圖片轉(zhuǎn)成Base64
,因為這種方式,即使說無網(wǎng)的情況也能正常加載圖片,因此我憑感覺斷定,這就是圖片同步加載
基于這個思路,我寫了個完整 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(`無法轉(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)換過程中出錯:', error);
});
}
</script>
此時就大功告成啦!不過得提一句:圖片的 URL 鏈接必須是同源或者允許跨越的,否則就會存在圖片加載異常的問題。
閱讀原文:原文鏈接
該文章在 2025/7/3 14:24:50 編輯過