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

ExpressJS中[ERR_HTTP_HEADERS_SENT]错误求助:人脸检测2FA接口异常

解决[ERR_HTTP_HEADERS_SENT]:重复发送HTTP响应的问题

错误根源

你的代码触发错误的核心原因是多个异步操作并行执行,且各自独立发送HTTP响应:

  • fs.access(含Python脚本调用)和photo.save是两个互不等待的异步任务,不管人脸检测逻辑是否完成,photo.save的回调都会先返回201响应;之后当Python脚本的stdout/stderr回调触发时,再次尝试发送响应就会触发Cannot set headers after they are sent to the client错误。
  • 此外,Python脚本的stderr事件可能在stdout之后触发,若stdout已经发送了响应,stderr再发送也会导致重复响应。

修复方案

要确保每个请求只发送一次响应,需将异步操作改为串行执行,并统一响应出口。以下是调整后的完整代码(以「先保存照片,再执行人脸检测」的业务逻辑为例):

create: function (req, res) {
    var photo = new PhotoModel({
        path: "/images/" + req.file.filename,
        postedBy: req.session.userId,
    });

    // 先完成照片保存,再执行人脸检测逻辑
    photo.save(function (err, savedPhoto) {
        if (err) {
            return res.status(500).json({
                message: 'Error when creating photo',
                error: err
            });
        }

        fs.access(`public/images/${req.session.userId}/${req.session.userId}.sav`, fs.constants.F_OK, (err) => {
            if (err) {
                // 无模型文件,执行模型创建脚本
                const createModelScript = spawn('python', ['python/faceCreateModel.py', `public/images/${req.session.userId}`, "python/faces_false", `public/images/${req.session.userId}/${req.session.userId}`]);
                
                let stdoutData = '';
                let stderrData = '';

                // 收集脚本输出
                createModelScript.stdout.on('data', (data) => {
                    stdoutData += data.toString();
                });
                
                createModelScript.stderr.on('data', (data) => {
                    stderrData += data.toString();
                });

                // 脚本执行完成后统一处理响应
                createModelScript.on('close', (code) => {
                    console.log('模型创建脚本退出码:', code);
                    if (stderrData) {
                        console.error('模型创建错误:', stderrData);
                        return res.status(500).json({
                            message: 'Error creating face model',
                            error: stderrData
                        });
                    }
                    console.log('模型创建结果:', stdoutData);
                    return res.status(201).json({
                        photo: savedPhoto,
                        message: 'Face model created successfully'
                    });
                });
            } else {
                // 有模型文件,执行人脸检测
                const files = fs.readdirSync(path.join('public/images', req.session.userId));
                const sortedFiles = files.map((filename) => {
                    const filePath = path.join('public/images', req.session.userId, filename);
                    const stat = fs.statSync(filePath);
                    return {
                        filename,
                        createdAt: stat.birthtimeMs,
                    };
                }).sort((a, b) => b.createdAt - a.createdAt);
                const recentImage = sortedFiles[0].filename;
                
                const faceTestScript = spawn('python', ['python/faceTestImage.py', `public/images/${req.session.userId}/${req.session.userId}.sav`, `public/images/${req.session.userId}/${recentImage}`]);
                
                let stdoutData = '';
                let stderrData = '';

                faceTestScript.stdout.on('data', (data) => {
                    stdoutData += data.toString();
                });
                
                faceTestScript.stderr.on('data', (data) => {
                    stderrData += data.toString();
                });

                faceTestScript.on('close', (code) => {
                    console.log('人脸检测脚本退出码:', code);
                    if (stderrData) {
                        console.error('人脸检测错误:', stderrData);
                        return res.status(500).json({
                            message: 'Error during face test',
                            error: stderrData
                        });
                    }
                    const result = stdoutData.trim();
                    console.log('人脸检测结果:', result);
                    if (result === 'true') {
                        return res.status(200).json({
                            photo: savedPhoto,
                            message: 'Face test passed successfully'
                        });
                    } else {
                        return res.status(400).json({
                            photo: savedPhoto,
                            message: 'Face test failed'
                        });
                    }
                });
            }
        });
    });
},

关键修改点

  1. 串行化异步操作:将photo.save作为执行入口,完成照片保存后再触发人脸检测逻辑,避免并行操作导致的响应冲突。
  2. 统一响应时机:不再在stdout/stderr事件触发时直接发送响应,而是收集所有输出后,监听脚本的close事件(脚本执行完成)再统一处理,避免多次触发响应。
  3. 单一响应出口:确保每个请求只会发送一次HTTP响应,彻底解决重复发送的问题。

额外建议

  • 如果业务逻辑要求「先通过人脸检测,再保存照片」,可以调整执行顺序:先完成人脸检测验证,通过后再调用photo.save并返回响应。
  • 确保Python脚本的输出格式稳定,比如true/false没有多余空格或换行,trim()方法可以有效处理这类问题。
  • 可以考虑用Promise封装异步操作,替代嵌套回调,提升代码可读性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:13:12