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

使用res.sendFile()时如何等待文件创建完成后再向客户端返回响应

文件生成后再返回客户端的解决方案

问题根因

carbone.render是异步回调API,回调内部的文件写入逻辑会被放入异步任务队列等待执行,而你放在回调外部的res.sendFile会同步优先执行,因此出现文件还没写入完成就尝试返回的问题。另外回调中的process.exit()会直接终止Node进程,正常服务场景下禁止使用该语句。

方案1:回调嵌套实现(最简改动)

直接把res.sendFile逻辑移动到carbone.render的回调内部,等文件写入完成后再执行返回逻辑,同时补充错误响应处理:

carbone.render("./template.odt", data, options, (err, ress) => {
  if (err) {
    console.log(err);
    return res.status(500).send('报告生成失败');
  }
  const timestamp = Date.now(); // 建议在回调内部生成timestamp,避免多请求冲突
  const paths = `./static/reports/report-${timestamp}.pdf`;
  try {
    fs.writeFileSync(paths, ress);
    // 写入完成后再执行返回
    res.sendFile(
      path.join(__dirname, "..", "static", "reports", `report-${timestamp}.pdf`)
    );
  } catch (writeErr) {
    console.log(writeErr);
    return res.status(500).send('文件写入失败');
  }
});
// 删掉原来写在外部的res.sendFile逻辑

方案2:async/await实现(更易维护)

使用Node内置的util.promisify将回调式API转为Promise,配合async/await语法编写,逻辑更清晰,避免回调嵌套:

const { promisify } = require('util');
const carboneRender = promisify(carbone.render);
const fsWriteFile = promisify(fs.writeFile);

// 你的路由处理函数需要加async修饰
async function handleReportReq (req, res) {
  try {
    const ress = await carboneRender("./template.odt", data, options);
    const timestamp = Date.now();
    const filePath = path.join(__dirname, "..", "static", "reports", `report-${timestamp}.pdf`);
    await fsWriteFile(filePath, ress);
    res.sendFile(filePath);
  } catch (err) {
    console.log(err);
    res.status(500).send('报告生成失败');
  }
}

注意事项

  • 建议在生成文件的逻辑内部生成timestamp,避免多个请求共用全局timestamp导致文件覆盖、读取错误
  • 生产环境建议定期清理生成的临时报告文件,避免磁盘空间被占满
  • 所有错误场景都要给客户端返回对应状态码和提示,不要仅在服务端打印日志

内容的提问来源于stack exchange,提问作者Arya Lisa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 07:39:02