NodeJS中使用pdfmake动态生成PDF并流式返回Blob的实现方法
在Node.js服务端用pdfmake生成PDF并返回Blob流(不保存到服务器)
我来帮你解决这个问题!你完全不需要把PDF保存到服务器,直接把生成的PDF流返回给前端,前端就能将其转换成Blob使用。下面是具体的实现方案,分服务端和前端两部分讲解:
服务端实现(以Express为例)
方式1:直接通过流传递(高效,适合大文件)
这种方式直接把pdfmake生成的流pipe到HTTP响应对象,不需要额外存储,性能更优:
const PdfPrinter = require('pdfmake'); const { PassThrough } = require('stream'); const express = require('express'); const app = express(); // 替换成你实际的字体配置 const fonts = { Roboto: { normal: 'fonts/Roboto-Regular.ttf', bold: 'fonts/Roboto-Bold.ttf', italics: 'fonts/Roboto-Italic.ttf', bolditalics: 'fonts/Roboto-BoldItalic.ttf' } }; app.get('/generate-pdf', (req, res) => { try { const printer = new PdfPrinter(fonts); // 定义你的PDF内容结构 const pdfContent = { content: [ '这是一个由pdfmake生成的测试PDF', { text: '加粗示例文本', bold: true }, { text: '多行文本示例', lineHeight: 1.6 } ] }; const pdfKitDoc = printer.createPdfKitDocument(pdfContent); // 这里就是你代码里"..."的位置:创建一个PassThrough流来中转PDF数据 const stream = new PassThrough(); // 将PDF文档流pipe到中转流,再pipe到响应对象 pdfKitDoc.pipe(stream); pdfKitDoc.end(); // 设置响应头,告诉客户端这是PDF文件 res.setHeader('Content-Type', 'application/pdf'); // 如果想让浏览器直接预览,用inline;如果默认下载,换成attachment res.setHeader('Content-Disposition', 'inline; filename="my-document.pdf"'); // 把流返回给前端 stream.pipe(res); } catch (err) { console.error('PDF生成失败:', err); res.status(500).send('生成PDF时发生错误'); } }); app.listen(3000, () => { console.log('服务启动在 http://localhost:3000'); });
方式2:收集数据块转Buffer(适合需要额外处理的场景)
如果你需要对PDF数据做一些额外处理(比如加密),可以先收集所有数据块,再转成Buffer返回:
const PdfPrinter = require('pdfmake'); const express = require('express'); const app = express(); const fonts = { /* 你的字体配置 */ }; app.get('/generate-pdf', (req, res) => { const printer = new PdfPrinter(fonts); const pdfContent = { /* 你的PDF内容 */ }; const pdfKitDoc = printer.createPdfKitDocument(pdfContent); const pdfChunks = []; // 监听data事件,收集所有数据块 pdfKitDoc.on('data', (chunk) => { pdfChunks.push(chunk); }); // 监听end事件,将数据块合并成Buffer返回 pdfKitDoc.on('end', () => { const pdfBuffer = Buffer.concat(pdfChunks); res.setHeader('Content-Type', 'application/pdf'); res.setHeader('Content-Disposition', 'inline; filename="my-document.pdf"'); res.send(pdfBuffer); }); // 处理生成过程中的错误 pdfKitDoc.on('error', (err) => { console.error('PDF生成出错:', err); res.status(500).send('生成PDF失败'); }); pdfKitDoc.end(); });
前端处理(获取Blob并使用)
前端通过fetch请求服务端接口,把响应转换成Blob,就可以用来预览、下载或者其他操作了:
async function fetchAndUsePdf() { try { const response = await fetch('http://localhost:3000/generate-pdf'); if (!response.ok) throw new Error('请求PDF失败'); // 将响应转换成Blob const pdfBlob = await response.blob(); // 示例1:在新窗口预览PDF const previewUrl = URL.createObjectURL(pdfBlob); window.open(previewUrl); // 示例2:创建下载链接 const downloadLink = document.createElement('a'); downloadLink.href = previewUrl; downloadLink.download = 'my-document.pdf'; downloadLink.click(); // 记得释放URL对象,避免内存泄漏 URL.revokeObjectURL(previewUrl); } catch (err) { console.error('处理PDF失败:', err); alert('获取PDF出错,请稍后重试'); } }
对你原有代码的说明
你代码里的pdfKitDoc.pipe( ... ),如果是在Express路由中,其实可以直接简化成pdfKitDoc.pipe(res),不需要额外的中转流(不过用PassThrough可以更灵活地处理流)。核心就是把pdfmake生成的PDF流直接传递给HTTP响应,前端就能拿到对应的Blob数据了。
内容的提问来源于stack exchange,提问作者Shewbii
相关产品推荐
相关产品推荐

