ExpressJS 和 PDFKit - 在内存中生成 PDF 并发送到客户端以供下载

ale*_*xtc 5 javascript node.js express node-pdfkit

在我的api路由器中,有一个名为的函数generatePDF,旨在使用 PDFKit 模块在内存中生成 PDF 文件并发送到客户端进行下载,而不是仅显示。

api.js

var express = require('express');
var router = express.Router();

const PDFDocument = require('pdfkit');

router.get('/generatePDF', async function(req, res, next) {
    var myDoc = new PDFDocument({bufferPages: true});
    myDoc.pipe(res);
    myDoc.font('Times-Roman')
         .fontSize(12)
         .text(`this is a test text`);
    myDoc.end();
    res.writeHead(200, {
        'Content-Type': 'application/pdf',
        'Content-disposition': 'attachment;filename=test.pdf',
        'Content-Length': 1111
    });
    res.send( myDoc.toString('base64'));
});

module.exports = router;
Run Code Online (Sandbox Code Playgroud)

这不起作用。错误信息是(node:11444) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

我怎样才能解决问题并让它发挥作用?

另外,一个相关的问题是如何将 PDF 生成的业务逻辑与路由器分开并将它们链接起来?

Ahm*_*dou 9

完整的解决方案。

var express = require('express');
var router = express.Router();

const PDFDocument =  require('pdfkit');

router.get('/generatePDF', async function(req, res, next) {
var myDoc = new PDFDocument({bufferPages: true});

let buffers = [];
myDoc.on('data', buffers.push.bind(buffers));
myDoc.on('end', () => {

    let pdfData = Buffer.concat(buffers);
    res.writeHead(200, {
    'Content-Length': Buffer.byteLength(pdfData),
    'Content-Type': 'application/pdf',
    'Content-disposition': 'attachment;filename=test.pdf',})
    .end(pdfData);

});

myDoc.font('Times-Roman')
     .fontSize(12)
     .text(`this is a test text`);
myDoc.end();
});

module.exports = router;
Run Code Online (Sandbox Code Playgroud)


Fre*_*sar 7

首先,我建议为 PDF 工具包创建一项服务。然后是您想要的路线的控制器。

我曾经get-stream让这件事变得更容易。

它还回答了您对已接受答案的问题:

如何将 PDF 生成的业务逻辑与路由器分离并将它们链接起来?

这是我的专业解决方案:

import PDFDocument from 'pdfkit';
import getStream from 'get-stream';
import fs from 'fs';


export default class PdfKitService {
  /**
   * Generate a PDF of the letter
   *
   * @returns {Buffer}
   */
  async generatePdf() {
    try {
      const doc = new PDFDocument();

      doc.fontSize(25).text('Some text with an embedded font!', 100, 100);

      if (process.env.NODE_ENV === 'development') {
        doc.pipe(fs.createWriteStream(`${__dirname}/../file.pdf`));
      }

      doc.end();

      const pdfStream = await getStream.buffer(doc);

      return pdfStream;
    } catch (error) {
      return null;
    }
  }
}

Run Code Online (Sandbox Code Playgroud)

然后是Controller的方法:

(...) 

  async show(req, res) {
    const pdfKitService = new PdfKitService();
    const pdfStream = await pdfKitService.generatePdf();

    res
      .writeHead(200, {
        'Content-Length': Buffer.byteLength(pdfStream),
        'Content-Type': 'application/pdf',
        'Content-disposition': 'attachment;filename=test.pdf',
      })
      .end(pdfStream);

 
  }
Run Code Online (Sandbox Code Playgroud)

最后是路线:

routes.get('/pdf', FileController.show);
Run Code Online (Sandbox Code Playgroud)