Express服务端生成PDF发送至前端文件损坏原因排查
解决前端下载PDF损坏但后端本地保存正常的问题
针对你遇到的情况:Express后端生成的PDF本地保存可正常打开,但前端下载后文件损坏(命名正确),以下是可能的原因及对应的解决方案:
1. 后端二进制数据发送方式错误
PDFDocument.save()返回的是Uint8Array类型的二进制数据,Express的res.send()在处理非Buffer类型的二进制时,会默认按UTF-8编码转换,导致原始二进制数据被篡改——这也是本地保存正常但前端下载损坏的核心原因(fs.writeFileSync直接写入原始二进制,不会进行编码转换)。
修复方法:将Uint8Array转换为Node.js的Buffer,使用res.end()发送(res.end()专为二进制数据设计,避免编码问题):
// 替换原res.send(pdfBytes)为: res.end(Buffer.from(pdfBytes));
2. 错误的Content-Type charset配置
PDF是二进制文件,不需要指定charset=utf-8。如果响应头包含该参数,浏览器可能错误解析二进制数据。
修复方法:确保Content-Type仅设置为application/pdf:
res.setHeader("Content-Type", "application/pdf"); // 移除任何; charset=utf-8相关配置
3. 前端文件名解析的引号残留
服务器返回的Content-Disposition头中,文件名带双引号(如attachment; filename="article_2024-05-20"),前端split后得到的文件名会包含双引号,部分浏览器处理时可能间接影响文件完整性。
修复方法:解析文件名时移除双引号:
const contentDisposition = response.headers.get('Content-Disposition'); const filename = contentDisposition.split('filename=')[1].replace(/["]/g, '');
4. 显式指定响应的二进制解析方式
虽然response.blob()通常能处理,但改用arrayBuffer()再转Blob,可确保二进制数据不被意外篡改:
// 替换原const blob = await response.blob()为: const arrayBuffer = await response.arrayBuffer(); const blob = new Blob([arrayBuffer], { type: 'application/pdf' });
修改后的完整代码示例
后端代码
app.get("/generate-pdf/:articleHref", async (req, res) => { const articleHref = req.params.articleHref; const pdfDoc = await PDFDocument.create(); const page = pdfDoc.addPage(); const { width, height } = page.getSize(); page.drawText("HELLO", { x: 50, y: height - 50, size: 20, color: rgb(0, 0, 0), }); const pdfBytes = await pdfDoc.save(); const currentDate = DateTime.now().toFormat('yyyy-MM-dd'); const fileName = `${articleHref}_${currentDate}.pdf`; // 直接添加.pdf后缀更可靠 res.setHeader("Content-Type", "application/pdf"); res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`); res.end(Buffer.from(pdfBytes)); // 关键修复 });
前端代码
const response = await fetch(`/generate-pdf/${articleHref}`, { method: 'GET' }); if (response.ok) { const arrayBuffer = await response.arrayBuffer(); const blob = new Blob([arrayBuffer], { type: 'application/pdf' }); const url = window.URL.createObjectURL(blob); const contentDisposition = response.headers.get('Content-Disposition'); const filename = contentDisposition.split('filename=')[1].replace(/["]/g, ''); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); }
内容的提问来源于stack exchange,提问作者Yana Mazo
相关产品推荐
相关产品推荐

