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

使用Multer实现文件上传时遇ERR_HTTP_HEADERS_SENT错误求助

解决Multer文件上传中ERR_HTTP_HEADERS_SENT错误

错误根源

  • 你注释掉的res.json如果放开,会在forEach循环的每个db.query回调里执行——HTTP协议要求一个请求只能返回一次响应,多次调用res.json直接触发错误。
  • forEach里的db.query是异步操作,循环不会等待所有查询完成,就算只在成功时发一次响应,也可能因为异步顺序问题导致重复发送。
  • multer错误处理后没加终止逻辑,后续代码可能继续执行,也会引发响应重复发送的问题。

修复步骤

  1. 用Promise封装数据库查询:把回调式的db.query转成Promise,方便批量处理异步操作。
  2. 统一响应时机:所有数据库插入完成后再发送成功响应,出错时立即终止流程并返回错误,确保只发送一次响应。
  3. 完善错误终止逻辑:multer出错时直接终止后续代码执行,避免无效操作。

修改后的完整代码

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "./routes/files");
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + "-" + file.originalname);
  },
});

const upload = multer({ storage }).fields([
  { name: "file", maxCount: 1 },
  { name: "school_id", maxCount: 1 },
]);

// 把db.query封装为Promise,方便异步控制
const queryPromise = (sql, params) => {
  return new Promise((resolve, reject) => {
    db.query(sql, params, (err, result) => {
      if (err) reject(err);
      else resolve(result);
    });
  });
};

router.post("/upload/students", async (req, res) => {
  try {
    // 把multer的回调转成Promise,配合async/await简化流程
    await new Promise((resolve, reject) => {
      upload(req, res, (err) => {
        if (err) {
          console.log(err);
          reject(err);
        } else {
          resolve();
        }
      });
    });

    const filePath = req.files["file"][0].path;
    const workbook = xlsx.readFile(filePath);
    const worksheet = workbook.Sheets[workbook.SheetNames[0]];
    const rows = xlsx.utils.sheet_to_json(worksheet);
    const school_id = req.body.school_id;
    console.log("SchoolId ", school_id);

    // 生成所有插入操作的Promise数组
    const insertPromises = rows.map((row) => {
      const student_id = row.student_id;
      const klass_id = row.klass;
      const stream_id = row.stream;
      const name = row.name;
      const year = row.year;

      return queryPromise(
        "INSERT INTO student(graduated, prefect, school_id,\
           name, student_id, klass_id, stream_id, year)\
        VALUES($1, $2, $3, $4, $5, $6, $7, $8 )",
        [0, 0, school_id, name, student_id, klass_id, stream_id, year]
      );
    });

    // 等待所有插入操作完成
    await Promise.all(insertPromises);
    // 统一发送成功响应
    res.json({ success: "Students Uploaded Successfully" });

  } catch (err) {
    console.log(err);
    // 统一处理所有错误,只发送一次错误响应
    res.json({ msg: err.message });
  }
});

module.exports = router;

关键改动说明

  • 用async/await替代嵌套回调,让异步流程更直观,避免回调地狱。
  • 用Promise.all批量处理所有数据库插入,确保所有操作完成后再返回响应,彻底解决多次发送响应的问题。
  • 所有错误集中到外层try/catch处理,保证错误响应只发送一次。
  • multer的错误直接通过reject进入外层捕获,终止后续无效代码执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 11:53:24