Reputation: 41
Intro.
Simple project written in Nest.js which allows to generate invoices.
@Controller('pdf')
export class PdfController {
constructor(private service: PdfService) { }
@Post('invoice')
@Header('Content-Type', 'application/pdf')
async generateInvoice(@Body() invoice: InvoiceDto): Promise<Buffer> {
return await this.service.generateInvoice(invoice);
}
}
@Injectable()
export class PdfService {
async generateInvoice(invoiceData: InvoiceDto): Promise<Buffer> {
const template = fs.readFileSync(join(__dirname, '..', 'pdf/templates/invoice.hbs'), 'utf-8');
const compiledTemplate = handlebars.compile(template);
return await this.generatePDF(compiledTemplate(invoiceData));
}
async generatePDF(html: string): Promise<Buffer> {
try {
const browser = await puppeteer;
const page = await browser.newPage();
await page.setContent(html);
return await page.pdf({format: 'A4'});
} catch (err) {
throw new HttpException(err, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
When I post to my endpoint on Postman I have followed response:
Everything looks like it should work, but when I save this PDF using Postman and tries to open it it says that file is corrupted.
I also tried to fetch it from a simple frontend using this method:
const downloadPDF = () => {
fetch('/pdf/invoice', {
body: JSON.stringify(testInvoice),
method: 'POST'
}).then(res => {
return res
.arrayBuffer()
.then(res => {
const blob = new Blob([res], { type: 'application/pdf' })
console.log(blob)
anchor.href = window.URL.createObjectURL(blob)
anchor.style.color = 'green'
})
.catch(e => alert(e))
})
}
But there is the same problem - it says that the file is corrupted.
IMPORTANT! When I save it in local dir by
return await page.pdf({path: 'invoice.pdf', format: 'A4'});
Then it works correctly...
Im really close to suicide - I've lost hours on that, I will be so grateful for help. Thank you in advance good people!
Upvotes: 1
Views: 2438
Reputation: 41
Solution! According to @hardcoded answer.
I refactored my controller:
@Post('invoice')
@Header('Content-Type', 'application/pdf')
async generateInvoice(
@Body() invoice: InvoiceDto,
@Res() res: Response,
) {
const buffer = await this.service.generateInvoice(invoice);
const stream = this.service.getReadableStream(buffer);
stream.pipe(res);
}
and then added new method which returns ReadableStream in my PdfService:
getReadableStream(buffer: Buffer): Readable {
const stream = new Readable();
stream.push(buffer);
stream.push(null);
return stream;
}
And that's it! Im recommending also to get into below handbook, it's very clear and complete how to dive deep into streams in Node.js:
https://github.com/substack/stream-handbook
Upvotes: 1