前言
最近,我们团队接到一个看似简单但背后充满挑战的需求:客户希望将HTML内容导出为PDF文件。原本这个任务应该由后端完成,但由于采集到的HTML内容可能存在标签不完整、嵌套混乱等问题,后端在处理时遇到了不少困难。于是,这个任务就落到了前端的肩上。
今天,我想和大家分享一下我们是如何通过前端技术,成功实现这个功能的。
问题背景:HTML内容的复杂性
在我们的项目中,后端从第三方抓取了大量HTML内容,这些内容可能包含未闭合的标签、不规范的嵌套结构,甚至是一些跨域图片。例如:
<h1>前端人</h1>
<p>学好前端,走遍天下都不怕</p>
<div>前端强,前端狂,交互特效我称王!</div
<p>JS 写得好,需求改不了!</p>
浏览器能很好地处理这些问题,但后端在尝试将这些HTML内容转换为PDF时,却频繁报错。后端尝试了多种第三方库,但都无法完美解决问题。
前端兜底:寻找解决方案
既然后端无法处理,那就轮到前端来兜底了。我们首先想到的是利用浏览器的打印功能,但这种方式需要用户手动操作,显然不符合客户的需求。
于是,我们开始寻找更直接的解决方案。经过一番调研,我们选择了html2pdf.js这个库,它可以将HTML内容直接转换为PDF文件,且支持自定义样式和布局。
实现步骤:HTML到PDF的转换
1. 安装依赖
首先,我们需要安装html2pdf.js:
npm install html2pdf.js
2. 基础实现
我们从一个简单的例子开始:
import html2pdf from 'html2pdf.js';
const element = `
<h1>前端人</h1>
<p>学好前端,走遍天下都不怕</p>
<p>JS 写得好,需求改不了!</p>
`;
function generatePDF() {
const opt = {
margin: 10,
filename: '前端大法好.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
};
html2pdf().from(element).set(opt).save();
}
这段代码会将HTML内容转换为PDF文件并保存到本地。
3. 解决图片加载问题
在实际场景中,HTML内容可能包含图片,而这些图片通常是通过异步加载的。如果直接导出,图片可能会显示为空白块。
为了解决这个问题,我们将图片转换为Base64格式,确保图片能够同步加载:
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);
resolve(canvas.toDataURL('image/png'));
};
img.onerror = () => reject(new Error('图片加载失败'));
img.src = url;
});
}
在导出PDF之前,我们先调用convertImagesToBase64方法,将HTML中的图片转换为Base64格式:
function generatePDF() {
const htmlContent = `
<style>
img {
max-width: 100%;
max-height: 100%;
vertical-align: middle;
height: auto !important;
width: auto !important;
margin: 10px 0;
}
</style>
<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>
`;
convertImagesToBase64(htmlContent)
.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);
});
}
通过这种方式,我们成功解决了图片加载问题,确保导出的PDF文件中图片能够正常显示。
总结
这次需求虽然看似简单,但背后涉及了HTML内容的复杂性、图片加载问题以及前后端协作的挑战。通过使用html2pdf.js,我们成功实现了HTML到PDF的转换,并解决了图片异步加载的问题。
前后端的协作是项目成功的关键。虽然这次是前端兜底,但我们也希望后端能够提供更多支持,共同解决问题。