如何通过GraphQL查询调用pdfmake实现PDF文件下载?
解决方案
你说得对,pdfmake的download()方法是前端浏览器环境专用的,后端(Node.js)没法直接调用它触发下载。后端要实现调用查询时触发下载,核心思路是生成PDF的二进制数据,然后通过响应头告诉浏览器这是一个需要下载的PDF文件,而非将文件存到服务器本地。
以下是修改后的Resolver代码,以Apollo Server(Express集成)为例:
import { Resolver, Query, Authorized, Ctx } from "type-graphql"; import Pdfmake from "pdfmake"; import { Readable } from "stream"; @Resolver() export class PdfResolver { @Authorized() @Query(() => String, { nullable: true }) // 可自定义标量类型,或直接处理响应 async CreatePdf(@Ctx() ctx: any) { const fonts = { Roboto: { normal: "fonts/roboto/Roboto-Regular.ttf", bold: "fonts/roboto/Roboto-Medium.ttf", italics: "fonts/roboto/Roboto-Italic.ttf", bolditalics: "fonts/roboto/Roboto-MediumItalic.ttf", }, }; const pdfmake = new Pdfmake(fonts); const docDefinition = { content: ["Hello World!"], }; const pdfDoc = pdfmake.createPdfKitDocument(docDefinition); // 收集PDF二进制数据到Buffer let chunks: Uint8Array[] = []; pdfDoc.on("data", (chunk: Uint8Array) => chunks.push(chunk)); pdfDoc.end(); return new Promise((resolve) => { pdfDoc.on("end", () => { const pdfBuffer = Buffer.concat(chunks); // 设置响应头触发浏览器下载 ctx.res.setHeader("Content-Type", "application/pdf"); ctx.res.setHeader("Content-Disposition", 'attachment; filename="test.pdf"'); // 将Buffer写入响应流 const stream = new Readable(); stream.push(pdfBuffer); stream.push(null); stream.pipe(ctx.res); resolve(null); // 已通过响应流返回数据,Resolver无需返回字符串 }); }); } }
关键改动说明:
- 移除
fs.writeStream,改为监听PDF文档的data事件收集二进制数据,最终合并为Buffer。 - 通过GraphQL的
@Ctx()获取Express响应对象(ctx.res),设置两个核心响应头:Content-Type: application/pdf:告知浏览器返回内容为PDF格式。Content-Disposition: attachment; filename="test.pdf":告知浏览器以附件形式下载文件,并指定文件名。
- 将PDF Buffer通过可读流写入响应,浏览器收到后会自动触发下载流程。
注意事项:
- 确保字体文件路径在后端环境中可访问,建议使用绝对路径避免路径问题。
- 若你的GraphQL服务器并非基于Express,需调整获取响应对象的方式(适配对应集成框架的API)。
- Resolver的返回类型可按需调整,比如自定义
PDF标量类型,或直接返回null(因已通过响应流返回数据)。
内容的提问来源于stack exchange,提问作者Jorge H
相关产品推荐
相关产品推荐

