Node后端向客户端传输PDF时出现空白页问题
这个问题的核心是前端错误地将二进制PDF数据转换成了文本字符串,同时后端和网关的一些设置也需要调整,最终导致下载的PDF文件结构损坏。咱们一步步来修复:
1. 前端代码修复(最关键的问题)
你现在用response.text()来处理PDF响应,这会把二进制数据强制解码成UTF-8字符串——但PDF是纯二进制格式,这个过程会破坏原始字节结构:比如一些无法用UTF-8表示的字节会被替换成占位符,再转成Blob时就完全不是原来的文件了。
修改前端代码:
- 把
response.text()换成response.blob(),直接获取二进制Blob对象; - 不需要手动创建Blob,直接用返回的blob生成ObjectURL即可。
修改后的代码:
// 修正retrievePDF函数 function retrievePDF () { return fetch('backendurlhere', { method: 'GET', // GET请求不需要设置Content-Type请求头,服务器会返回正确的类型 credentials: 'include' }) .then(response => response.blob()) // 直接获取二进制Blob .catch(err => console.log('ERR', err)) } // 修正React组件中的generatePDF方法 generatePDF () { this.props.retrievePDF() .then(pdfBlob => { const objectUrl = window.URL.createObjectURL(pdfBlob) window.open(objectUrl) }) }
2. Node后端(Restify)代码修复
你在createReadStream里设置了encoding: 'binary',这会让流输出的是字符串而非原始Buffer。Restify处理二进制时,直接用原始Buffer流即可,不需要指定编码。
修改后端代码:
export async function generatePDF (req, res, next) { try { // 移除encoding: 'binary',让流输出原始二进制Buffer const fStream = fs.createReadStream(path.join(__dirname, 'businesscard.pdf')) // 明确设置Content-Type为PDF res.setHeader('Content-Type', 'application/pdf') // 使用Restify的sendRaw方法直接传输二进制流,避免额外编码转换 res.sendRaw(fStream) next() } catch (err) { res.send(err) next() } }
注:Restify的
send()会自动处理编码逻辑,而sendRaw()更适合直接传输二进制数据,避免不必要的转换。
3. API网关设置修复
从你的HTTP响应头看到Content-Type: application/octet-stream,但网关应该明确设置为application/pdf,这样浏览器才能正确识别文件类型。确保网关要么保留后端返回的Content-Type,要么手动设置为application/pdf,不要替换成octet-stream。
另外,检查网关是否自动添加了Content-Encoding: gzip——如果后端没有对PDF做gzip压缩,网关的自动压缩可能会导致二进制数据损坏(虽然gzip本身合法,但最好由后端控制压缩逻辑)。
为什么之前的尝试无效?
你用readFileSync或手动拼接Buffer返回的方式没问题,但前端依然用response.text()处理,还是会把二进制转成字符串破坏结构。只有前端正确获取二进制Blob,才能保证文件完整。
验证修复效果
修复后,下载的PDF文件大小应该和原文件完全一致,用验证工具检测也不会再出现xref表损坏、Flate流错误等问题了。
内容的提问来源于stack exchange,提问作者Alaan

