前情提要
后台觉得生成好看的图表文件有点麻烦,想看看前端有没有好的方法
目标
前端技术生成基于echarts的pdf文件,数据读后台数据,返回pdf流文件给前端
尝试
纯前端感觉基本不可能,效率慢,适配度也不好,所以试试通过请求node去生成文件返回前端。
原理:
先生成html模板,html方便设样式,请求后台数据,填入html,再转成pdf返回给前端。 这里要使用到puppeteer,访问html再转成pdf。
Puppeteer 是一个 Node.js 库,它提供了一个高级 API 来控制 Chrome 或 Chromium 浏览器(通过 DevTools 协议)。Puppeteer 主要用于自动化操作浏览器,例如抓取网页、生成截图、生成 PDF、执行性能测试,甚至模拟用户交互等。它提供了对浏览器的无头(headless)操作,也支持在有界面模式下使用。
技术栈
- koa:接口转发
- axios:图表数据请求
- puppeteer:导出pdf
- ejs:html模板,方便填写数据
代码解析
router.get('/report/pdf', async (ctx) => {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
const data = response.data;
const templateData = {
title: data.title,
chartTitle: 'Sales Data',
xData: ['A', 'B', 'C', 'D', 'E', 'F'],
yData: [5, 20, 36, 10, 10, 5, 10]
};
// 将数据放入html模板 const templatePath = path.join(__dirname, 'template.ejs');
const html = await ejs.renderFile(templatePath, templateData);
//生成puppeteer实例
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
//等待html里面的网络请求完毕再setContent, html里需要下载echart的js
await page.setContent(html, { waitUntil: 'networkidle0' });
try {
const pdfBuffer = await page.pdf();
//也可以注释掉这个, 如果不需要本地也生成一份的话
await fs.writeFileSync('debug.pdf', pdfBuffer);
await browser.close();
ctx.set('Content-Type', 'application/pdf');
ctx.set('Content-Disposition', 'attachment; filename=report.pdf');
//需要用Buffer.from转成适应浏览器的格式,
//page.pdf()转出来的是node的文件流,给浏览器之前要先转格式再返回
//不然浏览器下载之后无法打开
ctx.body = Buffer.from(pdfBuffer) ;
} catch (err) {
console.error('PDF Error:', err); ctx.status = 500;
ctx.body = 'PDF generation failed';
}
});
后来
考虑到效率问题,还是没有用node去生成,确实生成单个图表的pdf感觉也比较慢,所以只能说能但效果不好,所以这个只是个demo,真正要用还要考虑挺多坑,比如网络请求和数据请求快慢的问题,效率的问题。 Puppeteer是个很有趣的库,有兴趣可以多了解一下。