使用Express API下载PDF



我正在创建一个作业列表scraper,它会去几个作业站点,抓取作业,然后将它们聚合在一起。我想创建一个端点,如果我喜欢一份工作,我可以自动下载我自己的求职信,其中包含基于该工作的某些动态数据。目前,我已经从谷歌文档中获取了求职信的HTML,并从中创建了一个字符串。我设置了一个GET端点,该端点应该获取这个HTML字符串,并将其作为可下载的PDF发送给请求者。目前,PDF正在下载中,但该文档称"无法加载PDF文档"。如果能为解决这一问题提供任何帮助,我们将不胜感激。

这是我的快递应用程序:

const express = require('express');
const dotenv = require('dotenv');
const app = express();
dotenv.config();
app.get('/cover-letter', (req, res) => {
const coverLetter = `<html><head><meta content="text/html; charset=UTF-8" http-equiv="content-type"><style type="text/css">@import url('https://themes.googleusercontent.com/fonts/css?kit=1ZpBgFLQKwrA6c9iLOONVLLukJZ0tncL9DlcRrH6sPk');ol{margin:0;padding:0}table td,table th{padding:0}.c1{border-right-style:solid;padding-top:0pt;border-top-width:0pt;border-right-width:0pt;padding-left:0pt;padding-bottom:0pt;line-height:1.0;border-left-width:0pt;border-top-style:solid;background-color:#ffffff;border-left-style:solid;border-bottom-width:0pt;border-bottom-style:solid;orphans:2;widows:2;text-align:justify;padding-right:0pt}.c21{border-right-style:solid;padding:0pt 5.4pt 0pt 5.4pt;border-bottom-color:#000000;border-top-width:0pt;border-right-width:0pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:0pt;border-top-style:solid;border-left-style:solid;border-bottom-width:0pt;width:311.4pt;border-top-color:#000000;border-bottom-style:solid}.c16{border-right-style:solid;padding:0pt 5.4pt 0pt 5.4pt;border-bottom-color:#000000;border-top-width:0pt;border-right-width:0pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:0pt;border-top-style:solid;border-left-style:solid;border-bottom-width:0pt;width:195.9pt;border-top-color:#000000;border-bottom-style:solid}.c19{padding-top:5pt;border-top-width:3pt;border-top-color:#000000;padding-bottom:0pt;line-height:1.0;orphans:2;border-top-style:solid;widows:2;text-align:left}.c5{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:normal}.c0{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:10pt;font-family:"Tahoma";font-style:normal}.c9{padding-top:0pt;padding-bottom:0pt;line-height:1.0;orphans:2;widows:2;text-align:left}.c7{padding-top:0pt;padding-bottom:0pt;line-height:1.0;orphans:2;widows:2;text-align:right}.c11{padding-top:0pt;padding-bottom:6pt;line-height:1.0;orphans:2;widows:2;text-align:left}.c18{padding-top:0pt;padding-bottom:0pt;line-height:1.0;orphans:2;widows:2;text-align:justify}.c12{padding-top:0pt;padding-bottom:0pt;line-height:1.1500000000000001;text-align:left;height:11pt}.c10{margin-left:-5.4pt;border-spacing:0;border-collapse:collapse;margin-right:auto}.c8{color:#000000;text-decoration:none;vertical-align:baseline;font-style:normal}.c2{vertical-align:baseline;font-size:10pt;font-family:"Tahoma";font-weight:400}.c22{font-weight:400;font-size:18pt;font-family:"Tahoma"}.c6{background-color:#ffffff;max-width:496.8pt;padding:57.6pt 57.6pt 57.6pt 57.6pt}.c15{font-weight:400;font-size:14.5pt;font-family:"Tahoma"}.c14{font-weight:400;font-size:11pt;font-family:"Times New Roman"}.c4{font-size:10pt;font-family:"Tahoma";font-weight:400}.c20{font-size:18pt;font-family:"Tahoma";font-weight:700}.c3{font-size:10.5pt;font-family:"Arial";font-weight:400}.c13{height:11pt}.c17{height:0pt}.title{padding-top:24pt;color:#000000;font-weight:700;font-size:36pt;padding-bottom:6pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}.subtitle{padding-top:18pt;color:#666666;font-size:24pt;padding-bottom:4pt;font-family:"Georgia";line-height:1.0;page-break-after:avoid;font-style:italic;orphans:2;widows:2;text-align:left}li{color:#000000;font-size:11pt;font-family:"Times New Roman"}p{margin:0;color:#000000;font-size:11pt;font-family:"Times New Roman"}h1{padding-top:24pt;color:#000000;font-weight:700;font-size:24pt;padding-bottom:6pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h2{padding-top:18pt;color:#000000;font-weight:700;font-size:18pt;padding-bottom:4pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h3{padding-top:14pt;color:#000000;font-weight:700;font-size:14pt;padding-bottom:4pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h4{padding-top:12pt;color:#000000;font-weight:700;font-size:12pt;padding-bottom:2pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h5{padding-top:11pt;color:#000000;font-weight:700;font-size:11pt;padding-bottom:2pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h6{padding-top:10pt;color:#000000;font-weight:700;font-size:10pt;padding-bottom:2pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}</style></head><body><div class="c6"><div><p class="c9 c13"><span class="c8 c14"></span></p></div><p class="c12"><span class="c5"></span></p><a id="t.cf2cb48f4187c740031c3f8d361f8f564f9a673c"></a><a id="t.0"></a><table class="c10"><tbody><tr class="c17"><td class="c21" colspan="1" rowspan="1"><p class="c11"><span class="c20">Test</span></p></td><td class="c16" colspan="1" rowspan="1"><p class="c7"><span class="c2">710 </span><span class="c4">Example</span><span class="c0">&nbsp;Dr. </span></p><p class="c7"><span class="c4">New York</span><span class="c2">, </span><span class="c4">New York</span><span class="c2">&nbsp;</span><span class="c4">55555</span></p><p class="c7"><span class="c2">m: </span><span class="c4">618</span><span class="c2">.</span><span class="c4">235</span><span class="c2">.</span><span class="c4">1234</span><span class="c0">&nbsp;</span></p><p class="c7"><span class="c4">example</span><span class="c0">@gmail.com</span></p></td></tr></tbody></table><p class="c13 c19"><span class="c8 c15"></span></p><p class="c9"><span class="c4">8/3/2020</span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9"><span class="c2">Dear </span><span class="c4">Hiring Manager</span><span class="c0">,</span></p><p class="c13 c18"><span class="c0"></span></p><p class="c1"><span class="c3 c8">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas cursus commodo felis. Suspendisse faucibus in lectus sed viverra. Curabitur turpis arcu, finibus vel porta et, commodo sit amet orci. Pellentesque rutrum finibus ipsum ac pharetra. Aenean varius purus erat, id rhoncus purus ornare eget. Nunc vel accumsan diam, sed sagittis augue. Aenean nec mauris dolor. Mauris vel pharetra magna. Nunc convallis id metus sed facilisis. In sed fringilla ligula.</span></p><p class="c1"><span class="c8 c3">Phasellus ut gravida nibh, vel dapibus lectus. Integer ut dui commodo tortor bibendum eleifend vel pharetra nunc. Donec at luctus leo. Praesent at tortor euismod, faucibus ante non, dignissim purus. Suspendisse sodales, lacus eu tincidunt sagittis, dui velit facilisis lacus, quis hendrerit arcu est ut urna. Praesent auctor arcu sit amet ullamcorper pellentesque. Sed dapibus quam eu lacus tempus, a luctus risus consequat. Cras id ipsum efficitur, feugiat sapien eu, mattis nunc. Proin iaculis ultricies ex ut volutpat. Vivamus sit amet consectetur nibh. Maecenas vel dictum ligula, varius porttitor elit. Suspendisse mauris nunc, sodales sit amet dignissim at, cursus dapibus est.</span></p><p class="c1"><span class="c8 c3">Nunc scelerisque efficitur diam non placerat. Aliquam enim velit, viverra id ante non, viverra aliquam quam. Quisque vel diam leo. Etiam tincidunt pellentesque mi sed eleifend. Fusce sollicitudin euismod enim, ac vestibulum metus interdum nec. Ut elementum metus in quam vehicula, sit amet scelerisque urna mollis. Praesent et arcu magna. Donec convallis sed urna vel euismod. Proin auctor faucibus leo mattis laoreet. Donec varius faucibus leo, non porttitor eros. Integer efficitur porta nunc eu ornare. Ut vel tellus nec nisi consequat dignissim.</span></p><p class="c1"><span class="c8 c3">Nunc dictum metus sed metus mattis aliquet. Vestibulum et justo tristique, fermentum metus ut, sagittis massa. Aliquam erat volutpat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Quisque ut eros ut ligula euismod gravida. Suspendisse consequat tincidunt urna vitae tempor. Nam rhoncus dapibus ante, at iaculis nisl molestie nec. Nunc tincidunt congue viverra.</span></p><p class="c1"><span class="c3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam viverra, nunc sit amet venenatis placerat, arcu risus imperdiet dolor, non vulputate metus nunc pellentesque nulla. Quisque pulvinar interdum maximus. Mauris quis est a nisl vulputate sagittis. Suspendisse laoreet a odio ac tincidunt. Donec suscipit, turpis blandit sollicitudin iaculis, urna justo faucibus velit, varius vehicula purus urna sed orci. Nulla facilisi. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</span></p><p class="c18"><span class="c0">Sincerely,</span></p><p class="c18 c13"><span class="c0"></span></p><p class="c9"><span class="c4">Test</span></p><p class="c9 c13"><span class="c0"></span></p><div><p class="c9 c13"><span class="c8 c14"></span></p></div></div></body></html>`

res.setHeader("Content-Type", "application/pdf");
res.status(200).send(coverLetter);
});
app.listen(process.env.PORT, () => {
console.log(`Listening on port ${process.env.PORT}...`)
})

您可以尝试使用puppeteer从html生成PDF文档。

下面的代码可以做到这一点,尽管我不得不去掉样式标签(可以在线添加,或者想办法让木偶师玩得很好(

const express = require('express');
const dotenv = require('dotenv');
const app = express();
dotenv.config();
async function generatePdf(html) { 
const puppeteer = require("puppeteer");
const browser = await puppeteer.launch({
args: ['--no-sandbox'],
headless: true
});
const page = await browser.newPage();
await page.goto(`data:text/html,${html}`, {
waitUntil: 'networkidle0'
});
const options = {
format: 'A4',
margin: { top: "60px", bottom: "60px" },
printBackground: true,
}
const pdf = await page.pdf(options);
await browser.close();
return pdf;
}
app.get('/cover-letter', async (req, res) => {
const coverLetter = `<html><head><meta content="text/html; charset=UTF-8" http-equiv="content-type"></head><body><div class="c6"><div><p class="c9 c13"><span class="c8 c14"></span></p></div><p class="c12"><span class="c5"></span></p><a id="t.cf2cb48f4187c740031c3f8d361f8f564f9a673c"></a><a id="t.0"></a><table class="c10"><tbody><tr class="c17"><td class="c21" colspan="1" rowspan="1"><p class="c11"><span class="c20">Test</span></p></td><td class="c16" colspan="1" rowspan="1"><p class="c7"><span class="c2">710 </span><span class="c4">Example</span><span class="c0">&nbsp;Dr. </span></p><p class="c7"><span class="c4">New York</span><span class="c2">, </span><span class="c4">New York</span><span class="c2">&nbsp;</span><span class="c4">55555</span></p><p class="c7"><span class="c2">m: </span><span class="c4">618</span><span class="c2">.</span><span class="c4">235</span><span class="c2">.</span><span class="c4">1234</span><span class="c0">&nbsp;</span></p><p class="c7"><span class="c4">example</span><span class="c0">@gmail.com</span></p></td></tr></tbody></table><p class="c13 c19"><span class="c8 c15"></span></p><p class="c9"><span class="c4">8/3/2020</span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9"><span class="c2">Dear </span><span class="c4">Hiring Manager</span><span class="c0">,</span></p><p class="c13 c18"><span class="c0"></span></p><p class="c1"><span class="c3 c8">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas cursus commodo felis. Suspendisse faucibus in lectus sed viverra. Curabitur turpis arcu, finibus vel porta et, commodo sit amet orci. Pellentesque rutrum finibus ipsum ac pharetra. Aenean varius purus erat, id rhoncus purus ornare eget. Nunc vel accumsan diam, sed sagittis augue. Aenean nec mauris dolor. Mauris vel pharetra magna. Nunc convallis id metus sed facilisis. In sed fringilla ligula.</span></p><p class="c1"><span class="c8 c3">Phasellus ut gravida nibh, vel dapibus lectus. Integer ut dui commodo tortor bibendum eleifend vel pharetra nunc. Donec at luctus leo. Praesent at tortor euismod, faucibus ante non, dignissim purus. Suspendisse sodales, lacus eu tincidunt sagittis, dui velit facilisis lacus, quis hendrerit arcu est ut urna. Praesent auctor arcu sit amet ullamcorper pellentesque. Sed dapibus quam eu lacus tempus, a luctus risus consequat. Cras id ipsum efficitur, feugiat sapien eu, mattis nunc. Proin iaculis ultricies ex ut volutpat. Vivamus sit amet consectetur nibh. Maecenas vel dictum ligula, varius porttitor elit. Suspendisse mauris nunc, sodales sit amet dignissim at, cursus dapibus est.</span></p><p class="c1"><span class="c8 c3">Nunc scelerisque efficitur diam non placerat. Aliquam enim velit, viverra id ante non, viverra aliquam quam. Quisque vel diam leo. Etiam tincidunt pellentesque mi sed eleifend. Fusce sollicitudin euismod enim, ac vestibulum metus interdum nec. Ut elementum metus in quam vehicula, sit amet scelerisque urna mollis. Praesent et arcu magna. Donec convallis sed urna vel euismod. Proin auctor faucibus leo mattis laoreet. Donec varius faucibus leo, non porttitor eros. Integer efficitur porta nunc eu ornare. Ut vel tellus nec nisi consequat dignissim.</span></p><p class="c1"><span class="c8 c3">Nunc dictum metus sed metus mattis aliquet. Vestibulum et justo tristique, fermentum metus ut, sagittis massa. Aliquam erat volutpat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Quisque ut eros ut ligula euismod gravida. Suspendisse consequat tincidunt urna vitae tempor. Nam rhoncus dapibus ante, at iaculis nisl molestie nec. Nunc tincidunt congue viverra.</span></p><p class="c1"><span class="c3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam viverra, nunc sit amet venenatis placerat, arcu risus imperdiet dolor, non vulputate metus nunc pellentesque nulla. Quisque pulvinar interdum maximus. Mauris quis est a nisl vulputate sagittis. Suspendisse laoreet a odio ac tincidunt. Donec suscipit, turpis blandit sollicitudin iaculis, urna justo faucibus velit, varius vehicula purus urna sed orci. Nulla facilisi. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</span></p><p class="c18"><span class="c0">Sincerely,</span></p><p class="c18 c13"><span class="c0"></span></p><p class="c9"><span class="c4">Test</span></p><p class="c9 c13"><span class="c0"></span></p><div><p class="c9 c13"><span class="c8 c14"></span></p></div></div></body></html>`
const pdfBuffer = await generatePdf(coverLetter);
res.setHeader("Content-Type", "application/pdf");
res.setHeader("Content-Disposition","inline; filename="test_resume_company_name.pdf"");
res.status(200).send(pdfBuffer);
});

app.listen(process.env.PORT, () => {
console.log(`Listening on port ${process.env.PORT}...`)
})

最新更新