You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 15:03:19