两跳系统中向客户端返回空白PDF的问题排查求助
问题描述
我搭建了一个两跳请求系统:客户端(浏览器或Postman)向我的Express服务器发起请求,服务器再发起二次请求获取PDF文件,最后将文件返回给原客户端。
测试环境下,以下ExpressJS代码可以正常返回可查看的PDF:
app.get('/download-test', async (_req, res) => { const fetchResponse = await fetch('https://link.testfile.org/PDF10MB'); if (fetchResponse.ok) { const blob = await fetchResponse.blob(); const buffer = await blob.arrayBuffer(); res.status(200); res.setHeader('Content-Type', 'application/pdf'); res.setHeader('Content-Disposition', 'attachment; filename="test.pdf"'); res.end(Buffer.from(buffer)); } else { res.status(404).send('Not found'); } });
但在实际业务服务器中实现相同逻辑时,返回的却是空白PDF,且文件大小比正确的字节数组/缓冲区大40KB(已通过日志确认原始字节数组大小是正确的)。查资料发现有些示例里Buffer.from需要指定编码,但我的测试代码没指定编码却能正常运行,请问问题出在哪,该怎么解决?
可能的原因与解决方案
1. 响应被意外转码(最可能)
返回文件体积变大,大概率是Express自动把二进制数据当成UTF-8字符串处理,导致二进制内容被转码损坏。测试环境没问题但生产环境出问题,通常是因为业务服务器开启了额外中间件(比如express.json()、express.urlencoded()或自定义字符编码中间件),这些中间件修改了响应的默认编码设置。
解决方法:
- 明确设置响应编码为
binary,或用res.send()代替res.end()发送Buffer,避免转码:
// 替换原res.end(Buffer.from(buffer))的写法 res.status(200) .setHeader('Content-Type', 'application/pdf') .setHeader('Content-Disposition', 'attachment; filename="test.pdf"') .send(Buffer.from(buffer));
- 更高效的方式:直接流式传输,避免把整个文件读进内存,同时彻底规避编码问题:
app.get('/download-test', async (_req, res) => { const fetchResponse = await fetch('https://link.testfile.org/PDF10MB'); if (fetchResponse.ok) { res.status(200) .setHeader('Content-Type', 'application/pdf') .setHeader('Content-Disposition', 'attachment; filename="test.pdf"'); // 直接将fetch的响应流pipe到Express响应 fetchResponse.body.pipe(res); } else { res.status(404).send('Not found'); } });
2. 缓冲区被意外篡改
确认实际业务代码中,arrayBuffer的来源和测试环境完全一致,没有被其他逻辑(比如日志、中间件)额外修改或转换。如果buffer变量不是标准的ArrayBuffer,Buffer.from可能会出现隐性错误。
3. 响应头冲突
检查业务服务器是否有其他中间件修改了Content-Type,或添加了Content-Encoding这类可能干扰PDF解析的响应头。可以用浏览器开发者工具对比测试环境和生产环境的响应头差异,排查异常配置。
内容的提问来源于stack exchange,提问作者icfantv
相关产品推荐
相关产品推荐

