如何配合使用html-pdf与jsZip将生成的PDF写入压缩包?
跑不通的核心原因
你之前的写法zip.file('newPDF.pdf', pdf.create(html).toStream())存在两个本质问题,根本没法拿到正确的PDF数据:
html-pdf的toStream()是带回调的异步方法,自身不会返回可读流实例,只会把生成的PDF流作为回调函数的入参传给你,你直接拿这个方法的调用返回值(一般是undefined)塞给zip.file(),自然传不进去任何有效数据- 就算你拿到了实际的流实例,JSZip虽然支持把可读流作为文件内容传入,也需要匹配对应的配置,否则会出现流还没读完、压缩包已经开始生成的时序问题,最终产出损坏的文件
正确实现方案
方案1:基于toStream的流写入(贴合你之前参考的示例写法)
按照html-pdf的原生回调语法,在回调内拿到真正的PDF流,再传给JSZip:
const pdf = require('html-pdf'); const JSZip = require('jszip'); const fs = require('fs'); const zip = new JSZip(); const htmlContent = '<p>需要转成PDF的HTML内容</p>'; // 严格遵循html-pdf的toStream回调语法 pdf.create(htmlContent).toStream((err, pdfReadableStream) => { if (err) throw err; // 拿到真实的PDF可读流后,直接作为文件内容传给zip zip.file('newPDF.pdf', pdfReadableStream); // 注意:生成zip流时必须开启streamFiles配置,否则无法正确消费传入的可读流 zip.generateNodeStream({ type: 'nodebuffer', streamFiles: true }).pipe(fs.createWriteStream('output.zip')) .on('finish', () => { console.log('压缩包生成完成'); }); });
方案2:基于Buffer的写入(更稳定,无流时序问题)
实际开发中更推荐这种方式,避开流传递容易出现的时序、阻塞问题,直接用html-pdf自带的toBuffer方法拿到完整的PDF二进制内容,再写入压缩包:
pdf.create(htmlContent).toBuffer((err, pdfBuffer) => { if (err) throw err; // Buffer是同步可用的,写入zip不会有时序问题 zip.file('newPDF.pdf', pdfBuffer); // 后续生成zip的逻辑和上面一致 zip.generateNodeStream({ type: 'nodebuffer' }) .pipe(fs.createWriteStream('output.zip')); });
常见踩坑提醒
- 绝对不要把
pdf.create(html).toStream()的直接返回值传给JSZip,这个方法本身没有返回值,所有数据都在回调里 - 用可读流传参给JSZip时,生成压缩包必须加
streamFiles: true配置,否则JSZip会等所有流全部读完才会开始生成包,大文件场景下会占满内存,甚至出现文件内容为空的问题 html-pdf是纯Node.js端的包,没法直接在浏览器端环境运行,如果是前端场景需要换用适配浏览器的PDF生成库
内容的提问来源于stack exchange,提问作者Cl0ser
相关产品推荐
相关产品推荐

