使用Multer实现文件上传时遇ERR_HTTP_HEADERS_SENT错误求助
解决Multer文件上传中
ERR_HTTP_HEADERS_SENT错误 错误根源
- 你注释掉的
res.json如果放开,会在forEach循环的每个db.query回调里执行——HTTP协议要求一个请求只能返回一次响应,多次调用res.json直接触发错误。 forEach里的db.query是异步操作,循环不会等待所有查询完成,就算只在成功时发一次响应,也可能因为异步顺序问题导致重复发送。- multer错误处理后没加终止逻辑,后续代码可能继续执行,也会引发响应重复发送的问题。
修复步骤
- 用Promise封装数据库查询:把回调式的
db.query转成Promise,方便批量处理异步操作。 - 统一响应时机:所有数据库插入完成后再发送成功响应,出错时立即终止流程并返回错误,确保只发送一次响应。
- 完善错误终止逻辑: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
相关产品推荐
相关产品推荐

