在网页开发中,实现 PDF 下载功能是常见的需求。以下是几种主流实现方式及其详细代码示例:
方案一,使用浏览器原生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>可打印内容</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转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>这是要导出为PDF的内容</h1>
<p>使用jsPDF和html2canvas库可以轻松实现HTML转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>
看着几乎完美,这个技术栈,最核心的就是:必须要用到 dom 元素渲染,试想一下,你做了一个导出功能,总不能让客户必须先打开页面等 html 渲染完后,再导出吧?或者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>学好前端,走遍天下都不怕</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是纯文本元素,这程序跑起来没有任何问题,但我们抓取的信息都源于互联网,html结构怎么可能会这么简单?如果我们的html中包含图片信息 ,此时你会发现,导出来的 pdf,图片占位处是个空白块
那我理解的图片同步加载是什么意思呢?简单来说,就是将图片转成Base64
,因为这种方式,即使说无网的情况也能正常加载图片,因此我凭感觉断定,这就是图片同步加载
基于这个思路,我写了个完整 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(`无法转换图片 ${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>职业:前端</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('转换过程中出错:', error);
});
}
</script>
此时就大功告成啦!不过得提一句:图片的 URL 链接必须是同源或者允许跨越的,否则就会存在图片加载异常的问题。
阅读原文:原文链接
该文章在 2025/7/3 14:24:50 编辑过