Express服务端无法向浏览器或Postman返回PDF文件问题咨询
问题诱因
- 异步流时序问题:PDFKit的文档生成、文件写入都是异步IO操作。你调用
doc.end()后,PDF内容还没有完全写入到本地的doc.pdf文件中,此时立即执行res.sendFile()读取的是不完整、甚至还未创建完成的文件,所以浏览器无法解析正常的PDF内容,抛出异常。 - 注释pipe后正常的假象:你注释掉写入本地文件的
doc.pipe逻辑后接口能返回,本质是读取了之前已经生成完成的旧doc.pdf文件。如果你删除本地已存在的doc.pdf再测试注释后的代码,同样会报错。 - 额外错误点:异常捕获块中
res.status(500).sendFile("Rahul")写法错误,sendFile需要传入合法的文件路径,传入普通字符串会触发额外的服务错误。
修复方案
根据是否需要保存PDF到本地磁盘,可选择两种方案:
方案1:无需保存PDF到本地(推荐,性能更高)
直接将PDF生成流对接Express的响应对象,不需要中转本地文件,同时设置正确的响应头让浏览器识别为PDF:
const PDF = require("pdfkit"); const express = require("express"); const app = express(); app.get("/", (req, res) => { try { const doc = new PDF({ size: "A4" }); // 设置响应头:inline为浏览器内打开,改为attachment则触发下载 res.setHeader("Content-Type", "application/pdf"); res.setHeader("Content-Disposition", "inline; filename=doc.pdf"); doc.image("/home/rahul/Desktop/projects/test/test/vlogo.png", 10, 0, { width: 100, height: 100, align: "left", }); // 直接把PDF流pipe到响应对象 doc.pipe(res); doc.end(); } catch (error) { console.log(error); res.status(500).send("服务内部错误"); } });
方案2:需要保留PDF到本地磁盘
监听文件写入流的finish事件,等文件完全写入完成后再调用res.sendFile()返回:
const PDF = require("pdfkit"); const fs = require("fs"); const express = require("express"); const app = express(); app.get("/", (req, res) => { try { const doc = new PDF({ size: "A4" }); const pdfPath = "/home/rahul/Desktop/projects/test/test/doc.pdf"; const writeStream = fs.createWriteStream(pdfPath); doc.image("/home/rahul/Desktop/projects/test/test/vlogo.png", 10, 0, { width: 100, height: 100, align: "left", }); doc.pipe(writeStream); doc.end(); // 监听写入完成事件再返回文件 writeStream.on('finish', () => { res.sendFile(pdfPath); }); // 监听写入异常 writeStream.on('error', (err) => { console.log(err); res.status(500).send("文件生成失败"); }); } catch (error) { console.log(error); res.status(500).send("服务内部错误"); } });
内容的提问来源于stack exchange,提问作者Rahul
相关产品推荐
相关产品推荐

