You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:05:01