Nest.js中Puppeteer生成PDF本地正常但Blob返回文件损坏求助
我之前也碰到过一模一样的问题!折腾了好久才搞定,大概率是二进制内容在传输或处理过程中被意外编码/修改导致的,咱们一步步来排查解决:
1. 补全响应头,避免客户端解析混乱
你已经设置了Content-Type,但建议再加上Content-Disposition头,明确告诉浏览器这是可下载的PDF文件,同时防止某些客户端对二进制内容做额外处理:
@Post('invoice') @Header('Content-Type', 'application/pdf') @Header('Content-Disposition', 'attachment; filename="invoice.pdf"') // 新增这行 async generateInvoice(@Body() invoice: InvoiceDto): Promise<Buffer> { return await this.service.generateInvoice(invoice); }
2. 改用Nest.js推荐的StreamableFile返回二进制内容
直接返回Buffer有时候会被全局拦截器/自动编码搞砸,用StreamableFile能确保Nest.js正确处理二进制流,避免不必要的转换:
首先引入依赖:
import { StreamableFile } from '@nestjs/common'; import { PassThrough } from 'stream';
然后修改控制器方法:
async generateInvoice(@Body() invoice: InvoiceDto): Promise<StreamableFile> { const pdfBuffer = await this.service.generateInvoice(invoice); // 把Buffer转成可读流 const stream = new PassThrough(); stream.end(pdfBuffer); return new StreamableFile(stream, { type: 'application/pdf', disposition: 'attachment; filename="invoice.pdf"', }); }
3. 检查前端fetch请求的responseType设置
前端如果没指定responseType: 'arraybuffer',会默认把响应当成文本解析,直接破坏PDF的二进制结构,一定要加上:
fetch('/pdf/invoice', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(yourInvoiceData), }) .then(response => { // 必须先确认响应状态正常 if (!response.ok) throw new Error('请求失败'); return response.arrayBuffer(); // 这里一定要用arrayBuffer,不能用text() }) .then(buffer => { const blob = new Blob([buffer], { type: 'application/pdf' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'invoice.pdf'; a.click(); URL.revokeObjectURL(url); // 记得释放内存 });
4. 验证服务生成的Buffer本身是否正常
先排除服务端生成的PDF有问题:在PdfService里临时把生成的Buffer写入本地文件,看看能不能正常打开:
async generateInvoice(invoiceData: InvoiceDto): Promise<Buffer> { // ... 现有代码 const pdfBuffer = await page.pdf({format: 'A4'}); // 临时写入本地验证 fs.writeFileSync('test-local.pdf', pdfBuffer); return pdfBuffer; }
如果这个本地文件能正常打开,说明服务端生成没问题,问题肯定出在响应传输或前端处理。
5. 排查全局拦截器/过滤器是否篡改了响应
如果你的项目里有全局响应拦截器(比如统一返回{ code: 200, data: ... }的格式),那它会把PDF Buffer包裹成JSON对象,直接破坏二进制内容。要给这个PDF接口加个例外:
- 在控制器方法上用
@SkipInterceptor(YourGlobalInterceptor)跳过拦截(如果你的拦截器支持) - 或者在拦截器逻辑里判断请求路径,比如如果是
/pdf/invoice就直接返回原响应,不做包装。
按照上面的步骤走,基本就能解决问题了!我当时就是因为全局拦截器把PDF包成了JSON,导致下载的文件全是乱码😂
内容的提问来源于stack exchange,提问作者Jakub Wąsowski
相关产品推荐
相关产品推荐

