Express.js中PDFkit缓冲流经pdf2pic生成的PNG图片损坏问题
问题:Express生成的PNG图片损坏无法正常显示
我用Express.js搭建了HTTP服务器,接收包含文本的POST请求:通过PDFkit生成对应内容的PDF,不保存到本地而是用流缓冲数据,之后将缓冲数据传入pdf2pic转换为PNG返回给客户端。但客户端保存后的图片显示损坏(文件体积过小),调试后未找到根源,怀疑是缓冲数据不完整或提前发送。
前端axios请求代码(使用FileSaver的saveAs保存PNG)
const requestData = { text: gift.value.text, }; const config = { headers: { 'Content-Type': 'application/json' }, responseType: 'arraybuffer' }; axios.post('/png', JSON.stringify(requestData), config) .then(response => { const blob = new Blob([await res.data], { type: 'image/png' }) await saveAs(blob, fileName) }) .catch(error => { // Handle errors console.error(error); });
后端Express处理POST请求代码
import type { Request, Response } from 'express' import express from 'express' import bodyParser from 'body-parser' import * as fs from 'fs' import * as stream from 'stream' import PDFDocument from 'pdfkit' import { fromBuffer } from 'pdf2pic' const router = express.Router() router.post('/png', bodyParser.json(), async (req: Request, res: Response) => { const { text } = req.body if (!text) { return res.status(400).json({ error: 'Text is required in the request body' }) } // 创建PDF文档 const pdfDoc = new PDFDocument() // 创建用于存储PDF内容的可写流 const pdfStream = new stream.Writable() const pdfBuffer: Buffer[] = [] pdfStream._write = (chunk, encoding, callback) => { pdfBuffer.push(chunk) callback() } pdfStream.on('finish', async () => { const pdf2pic = fromBuffer(Buffer.concat(pdfBuffer), { density: 100, saveFilename: 'output', savePath: './', format: 'png', width: 600, height: 800 }) const result = await pdf2pic(1) // 转换第一页 console.log(result) // 将PNG图片返回给客户端 res.contentType('image/png') res.send(result) // 清理临时文件 fs.unlinkSync('./') }) pdfDoc.pipe(pdfStream) pdfDoc.text(text) pdfDoc.end() }) export { router as services }
损坏的图片

更新1:测试PDF缓冲流可用性
按照建议直接返回PDF缓冲流,确认可用,测试代码如下:
router.post('/png', bodyParser.json(), async (req: Request, res: Response) => { const { text } = req.body if (!text) { return res.status(400).json({ error: 'Text is required in the request body' }) } // 创建PDF文档 const pdfDoc = new PDFDocument() // 创建用于存储PDF内容的可写流 const pdfStream = new stream.Writable() const pdfBuffer: Buffer[] = [] pdfStream._write = (chunk, encoding, callback) => { pdfBuffer.push(chunk) callback() } pdfStream.on('finish', async () => { // 设置内容类型为PDF res.contentType('application/pdf') // 将PDF缓冲直接发送到响应 res.send(Buffer.concat(pdfBuffer)) }) pdfDoc.pipe(pdfStream) pdfDoc.text(text) pdfDoc.end() })
错误分析与修复方案
1. 前端异步逻辑错误
前端then回调未声明为异步函数,且错误使用res.data(应为response.data),导致无法正确获取二进制数据。修改后代码:
axios.post('/png', JSON.stringify(requestData), config) .then(async (response) => { const blob = new Blob([response.data], { type: 'image/png' }) await saveAs(blob, fileName) })
2. 后端pdf2pic返回值处理错误
pdf2pic(1)返回的是包含图片路径和缓冲区的对象,直接res.send(result)会把对象序列化为JSON发送,而非图片二进制数据,需改为发送result.buffer:
const result = await pdf2pic(1) res.contentType('image/png') res.send(result.buffer) // 发送图片二进制缓冲区
3. 无效的文件清理操作
fs.unlinkSync('./')尝试删除目录会报错,因为unlinkSync仅支持删除文件。若不需要保存本地图片,可修改pdf2pic配置禁用本地存储;若需保存,应删除生成的具体文件:
// 禁用本地存储的配置 const pdf2pic = fromBuffer(Buffer.concat(pdfBuffer), { density: 100, format: 'png', width: 600, height: 800, savePath: false // 不保存到本地 }) // 若需保存后删除 fs.unlinkSync(result.path)
4. 自定义可写流的简化优化
使用stream.PassThrough替代自定义Writable Stream,更简洁可靠:
const pdfBuffer: Buffer[] = [] const passThrough = new stream.PassThrough() passThrough.on('data', chunk => pdfBuffer.push(chunk)) passThrough.on('end', async () => { // 此处执行PDF转PNG及响应逻辑 }) pdfDoc.pipe(passThrough) pdfDoc.text(text) pdfDoc.end()
内容的提问来源于stack exchange,提问作者richardevcom
相关产品推荐
相关产品推荐

