javascript 在 Node.js 中使用远程图像提供动态生成的 PDF

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22887464/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 23:57:54  来源:igfitidea点击:

Serve dynamically generated PDF with remote images in Node.js

javascriptnode.jsexpressnode-pdfkit

提问by Tyler Eich

I'm trying to create a Node server that generates a PDF on-the-fly using PDFKit. The PDF is generated based on parameters from a POST request (via Express). One of the parameters specifies an image URL, which the server downloads and injects into the PDF.

我正在尝试创建一个 Node 服务器,该服务器使用PDFKit即时生成 PDF 。PDF 是基于来自 POST 请求(通过Express)的参数生成的。其中一个参数指定了一个图像 URL,服务器下载该 URL 并将其注入到 PDF 中。

Right now, I have the following structure:

现在,我有以下结构:

// Get dependencies
var express = require('express'),
http = require('http'),
fs = require('fs'),
pdfDocument = require('pdfkit');

// Get express started.
var app = express();

// Use JSON in POST body
app.use(express.json());

// Setup POST response
app.post('/post_pdf', function(req, res) {
    // Get the PDF initialized
    var doc = new pdfDocument();

    // Set some headers
    res.statusCode = 200;
    res.setHeader('Content-type', 'application/pdf');
    res.setHeader('Access-Control-Allow-Origin', '*');

    // Header to force download
    res.setHeader('Content-disposition', 'attachment; filename=Untitled.pdf');     

    // Pipe generated PDF into response
    doc.pipe(res);

    /**
     * Generate PDF contents
     */

    // Prepare write stream for image
    var image = fs.createWriteStream('image.jpeg');

    // Download image
    http.get("http://dummyimage.com/640.jpeg", function(response) {

        // Pipe response into image write stream
        // (because PDFKit needs to read from a saved file)
        response.pipe(image).on('close', function() {

            // Read data back, make sure there are no errors
            fs.readFile('image.jpeg', function(err, data) {
                if (err) throw err;

                /**
                 * Use `data` to get image info (width, height, etc.)
                 * ------------------
                 * Inject image
                 */

                // Close document and response
                doc.end();
                res.end();
                return;
            })
        });
    });
});

I have two questions:

我有两个问题:

  • Is there a less messy way to do this, perhaps with fewer nested callbacks? I'm totally open to adding another dependency to make life easier.
  • Right now, the code above does not work. It returns a PDF, but the PDF is corrupted (according to Preview). Any tips as to why this could be occurring are very welcome.
  • 有没有更简洁的方法来做到这一点,也许嵌套回调更少?我完全愿意添加另一个依赖项以使生活更轻松。
  • 现在,上面的代码不起作用。它返回一个 PDF,但 PDF 已损坏(根据预览)。任何有关为什么会发生这种情况的提示都非常受欢迎。

回答by Tyler Eich

In debugging this issue, I discovered several things:

在调试这个问题时,我发现了几件事:

PDFKit does not need to read info from a file. It will also accept a Buffer

PDFKit 不需要从文件中读取信息。它还将接受一个Buffer

doc.image(myBuffer); // You don't have to use a path string


When piping a file directly into the response, a manual call to response.end()will cause problems if the file has already been closed

将文件直接通过管道传输到响应中时,response.end()如果文件已关闭,则手动调用将导致问题

doc.pipe(res); // Pipe document directly into the response

doc.end(); // When called, this ends the file and the response

// res.end(); <-- DON'T call res.end()
//                The response was already closed by doc.end()
return;


Requestis a super-useful NodeJS library that can flatten the callback tree

Request是一个超级好用的 NodeJS 库,可以压平回调树



Updated code:

更新代码:

var express = require('express'),
request = require('request'),
pdfDocument = require('pdfkit');

// Start Express
var app = express();

// Use JSON in POST body
app.use(express.json());

// Setup POST response
app.post('/post_pdf', function(req, res) {
    // Create PDF
    var doc = new pdfDocument();

    // Write headers
    res.writeHead(200, {
        'Content-Type': 'application/pdf',
        'Access-Control-Allow-Origin': '*',
        'Content-Disposition': 'attachment; filename=Untitled.pdf'
    });

    // Pipe generated PDF into response
    doc.pipe(res);

    // Process image
    request({
        url: 'http://dummyimage.com/640.jpeg',
        encoding: null // Prevents Request from converting response to string
    }, function(err, response, body) {
        if (err) throw err;

        // Inject image
        doc.image(body); // `body` is a Buffer because we told Request
                         // to not make it a string

        doc.end(); // Close document and, by extension, response
        return;
    });
});