You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

生成PDF并发送邮件后,删除PDF提示文件不存在的问题

解决方案

问题根源

  1. PDF生成未等待文件写入完成:pdfTicketGenerator中调用doc.end()后直接返回文件名,但文件流可能仍在向磁盘写入数据,后续操作基于“文件已完成”的假设执行,导致时序错误。
  2. 无效代码引发异常:sendBoughtTicketEmail中的await Promise.all(pdfName);是错误代码,pdfName为字符串而非Promise数组,会干扰正常流程。
  3. 同步文件操作的状态不一致: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 16:30:45