生成PDF并发送邮件后,删除PDF提示文件不存在的问题
解决方案
问题根源
- PDF生成未等待文件写入完成:
pdfTicketGenerator中调用doc.end()后直接返回文件名,但文件流可能仍在向磁盘写入数据,后续操作基于“文件已完成”的假设执行,导致时序错误。 - 无效代码引发异常:
sendBoughtTicketEmail中的await Promise.all(pdfName);是错误代码,pdfName为字符串而非Promise数组,会干扰正常流程。 - 同步文件操作的状态不一致:
fs.unlinkSync等同步操作在文件流未完全关闭时执行,可能出现文件系统状态不匹配的问题。
修复步骤
1. 修正PDF生成函数,确保文件完全写入
修改pdf-bought-ticket-provider.ts中的pdfTicketGenerator,将文件流的finish事件包装为Promise,等待写入完成后再返回文件名:
export const pdfTicketGenerator = async ( input: IPdfTicket, pathToSave: string, ): Promise<string> => { const doc = new PDFDoc({ autoFirstPage: false }); const pdfName = `${idGeneratorProvider.generateUUID()}.pdf`; const pathToPdf = path.resolve(pathToSave, pdfName); const writer = fs.createWriteStream(pathToPdf); doc.pipe(writer); if (input.promoterAlert) { doc.addPage(); doc.fontSize(20).text('Nota do produtor:', 210, 50); doc.fontSize(15).text(input.promoterAlert, 50, 100); } // 处理ticket内容(保持原有逻辑,若为同步操作无需Promise) input.tickets.forEach((ticket) => { // bulk of pdf write... }); doc.end(); // 等待文件流完成写入 await new Promise((resolve, reject) => { writer.on('finish', resolve); writer.on('error', reject); }); console.log( 'PDF已完成写入', Date.now(), '文件名前缀:', pdfName.split('-')[0], '文件是否存在:', fs.existsSync(pathToPdf), '文件路径:', pathToPdf, ); return pdfName; };
2. 移除无效代码并优化删除逻辑
修改send-bought-ticket-email.ts:
- 删除
await Promise.all(pdfName);错误代码 - 将同步删除改为异步操作,提升稳定性
// ... 其他代码保持不变 const pdfName = await pdfTicketGenerator( { tickets: qrCodes, eventName: input.params.event.name, localDescription: input.params.event.address, localName: input.params.event.localName, ownerTicketName: input.params.owner.name.toUpperCase(), payedAt: dateFormatter(input.params.order.payedAt), startDateHour: dateFormatter(input.params.event.start), promoterAlert: input.params.event.producerMessage, }, pathToTemp, ); // 移除此行错误代码 // await Promise.all(pdfName); // ... 邮件发送代码保持不变 // 异步删除二维码文件 const qrCodesUnlinkPromises = qrCodes.map((qrCode) => { const pathFile = path.resolve(pathToTemp, qrCode.filename); return fs.promises.unlink(pathFile).catch(err => { console.warn('删除二维码文件失败:', err.message); }); }); await Promise.all(qrCodesUnlinkPromises); // 异步删除PDF文件 const pdfPath = path.resolve(pathToTemp, pdfName); try { await fs.promises.access(pdfPath); await fs.promises.unlink(pdfPath); console.log('PDF文件已成功删除'); } catch (err) { console.warn('删除PDF文件失败:', err.message); }
3. 额外优化建议
- 确保临时目录存在:在代码开头添加
await fs.promises.mkdir(pathToTemp, { recursive: true });,避免目录不存在导致的文件生成失败。 - 确认邮件发送函数为异步:确保
emailProvider.sendGeneric返回Promise并被正确await,保证邮件发送完成后再执行删除操作。
内容的提问来源于stack exchange,提问作者Shedyhs
相关产品推荐
相关产品推荐

