[英]How to render 1k pdf files using jsreport and jsreport Chrome Pdf?
I'm trying to be able to generate 1k reports using and endpoint with Express.js, I pass an array in a JSON, the API goes over it and in a forEach loop, then every object is used to scrap a portal, get the response, and create a PDF file... 我试图使用Express.js生成端点报告,并使用Express.js生成端点,我在JSON中传递数组,API遍历它并在forEach循环中使用,然后将每个对象用于抓取门户,获取响应,然后创建一个PDF文件...
This approach pseudo work, but I'm pretty sure that there are some concurrency problems... because, if I pass 2 items in the JSON array the API can create the 2 PDF files without a problem, but if I pass 300 the API creates randomly 50... or 60 or 120. 这种方法是伪工作,但是我可以肯定存在一些并发问题...因为,如果我在JSON数组中传递2个项目,那么API可以毫无问题地创建2个PDF文件,但是如果我传递300个API,随机创建50 ...或60或120。
This is my jsreport config 这是我的jsreport配置
const jsReportConfig = {
extensions: {
"chrome-pdf": {
launchOptions: {
timeout: 10000,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
},
},
},
tempDirectory: path.resolve(__dirname, './../../temporal/pdfs'),
templatingEngines: {
numberOfWorkers: 4,
timeout: 180000,
strategy: 'http-server',
},
};
I setup the jsreport instance like this 我像这样设置jsreport实例
jsreport.use(jsReportChrome());
jsreport.use(jsReportHandlebars());
jsreport.init()
And, this is how I render the reports, the checkInvoiceStatus
function is used as an HTTP call that returns an HTML response that is injected in the Handlebars template. 而且,这就是我呈现报告的方式,
checkInvoiceStatus
函数用作HTTP调用,该HTTP返回返回注入到Handlebars模板中的HTML响应。
const renderReports = (reporter, invoices) => new Promise(async (resolve, reject) => {
try {
const templateContent = await readFile(
path.resolve(__dirname, './../templates/hello-world.hbs'),
'utf-8',
);
invoices.forEach(async (invoice) => {
try {
const response = await checkInvoiceStatus(invoice.re, invoice.rr, invoice.id)
const $ = await cheerio.load(response);
const reporterResponse = await reporter.render({
template: {
content: templateContent,
engine: 'handlebars',
recipe: 'chrome-pdf',
name: 'PDF Validation',
chrome: {
displayHeaderFooter: true,
footerTemplate: '<table width=\'100%\' style="font-size: 12px;"><tr><td width=\'33.33%\'>{#pageNum} de {#numPages}</td><td width=\'33.33%\' align=\'center\'></td><td width=\'33.33%\' align=\'right\'></td></tr></table>',
},
},
data: {
taxpayerId: 'CAC070508MY2',
captcha: $('#ctl00_MainContent_ImgCaptcha').attr('src'),
bodyContent: $('#ctl00_MainContent_PnlResultados').html(),
},
});
reporterResponse.result.pipe(fs.createWriteStream(`./temporal/validatedPdfs/${invoice.id}.pdf`));
} catch (err) {
console.error(err);
reject(new Error(JSON.stringify({
code: 'PORTAL-PDFx001',
message: 'The server could not retrieve the PDF from the portal',
})));
}
});
resolve();
} catch (err) {
console.error(err);
reject(new Error(JSON.stringify({
code: 'PORTAL-PDFx001',
message: 'The server could not retrieve the PDF from the portal',
})));
}
});
I don't know why, but this function is terminated in 500ms , but the files are been created after 1 minute... 我不知道为什么,但是此功能在500毫秒后终止,但是文件是在1分钟后创建的...
app.post('/pdf-report', async (req, res, next) => {
const { invoices } = req.body;
repository.renderReports(reporter, invoices)
.then(() => res.status(200).send('Ok'))
.catch((err) => {
res.status(500).send(err);
});
});
UPDATE UPDATE
Alongside the code presented by @hurricane, I had to change the jsReport config to this 除了@hurricane提供的代码外,我还必须将jsReport配置更改为此
const jsReportConfig = {
chrome: {
timeout: 180000,
strategy: 'chrome-pool',
numberOfWorkers: 4
},
extensions: {
'chrome-pdf': {
launchOptions: {
timeout: 180000,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
ignoreDefaultArgs: ['--disable-extensions'],
},
},
},
tempDirectory: path.resolve(__dirname, './../../temporal/pdfs'),
templatingEngines: {
numberOfWorkers: 4,
timeout: 180000,
strategy: 'http-server',
},
};
I think in your structure it is easy to solve your problem with writeFileSync
function instead of using writeStream. 我认为在您的结构中,使用
writeFileSync
函数而不是使用writeStream可以轻松解决问题。 But it does not mean it is the best approach. 但这并不意味着这是最好的方法。 Because you have to wait for each render and each write file process to start another one.
因为您必须等待每个渲染和每个写入文件进程开始另一个进程。 So i would suggest to use
Promise.all
that you can run your long processes at the same time. 因此,我建议使用
Promise.all
,以便您可以同时运行较长的流程。 That means you will wait only for longest process. 这意味着您将只等待最长的过程。
Quick win - Slow process 快速获胜-流程缓慢
reporterResponse.result.pipe
Change this line with 更改此行
fs.createFileSync(`./temporal/validatedPdfs/${invoice.id}.pdf`);
Better Approach - Fast process 更好的方法-快速的过程
const renderReports = (reporter, invoices) => new Promise(async (resolve, reject) => {
try {
const templateContent = await readFile(
path.resolve(__dirname, './../templates/hello-world.hbs'),
'utf-8',
);
const renderPromises = invoices.map((invoice) => {
const response = await checkInvoiceStatus(invoice.re, invoice.rr, invoice.id)
const $ = await cheerio.load(response);
return await reporter.render({
template: {
content: templateContent,
engine: 'handlebars',
recipe: 'chrome-pdf',
name: 'PDF Validation',
chrome: {
displayHeaderFooter: true,
footerTemplate: '<table width=\'100%\' style="font-size: 12px;"><tr><td width=\'33.33%\'>{#pageNum} de {#numPages}</td><td width=\'33.33%\' align=\'center\'></td><td width=\'33.33%\' align=\'right\'></td></tr></table>',
},
},
data: {
taxpayerId: 'CAC070508MY2',
captcha: $('#ctl00_MainContent_ImgCaptcha').attr('src'),
bodyContent: $('#ctl00_MainContent_PnlResultados').html(),
},
});
});
const renderResults = await Promise.all(renderPromises);
const filewritePromises = results.map(renderedResult => await fs.writeFile(`./temporal/validatedPdfs/${invoice.id}.pdf`, renderedResult.content));
const writeResults = await Promise.all(filewritePromises);
resolve(writeResults);
} catch (err) {
console.error(err);
reject(new Error(JSON.stringify({
code: 'PORTAL-PDFx001',
message: 'The server could not retrieve the PDF from the portal',
})));
}
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.