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

请求Express端点持续加载无法返回响应(Multer与Express问题)

问题分析:Multer跨端点文件上传错误无法返回状态码

背景与流程

我正在基于Multer提供的文件buffer获取文件签名来实现过滤器(不单纯依赖Multer返回的MIME类型),操作流程如下:

  • 使用Multer的memoryStorage()临时存储请求文件以获取buffer;
  • 过滤有效文件;
  • 将有效文件及所需其他信息添加到新创建的FormData中;
  • 用新FormData向另一个端点("/upload")发起POST请求;
  • 通过Multer将文件存储到云端。

问题描述

当第二个端点触发错误时,流程陷入循环,错误已被检测到,但无法向第一个端点返回任何HTTP状态码!

代码片段

第一个端点(/filesUpload)

/* 注意:storeInMemory.array("files")会将文件的buffer信息存储在内存中,
/* 这是验证文件所必需的! */
app.post("/filesUpload", storeInMemory.array("files"), async (req, res) => {
    const files = req.files;

    /* 这个数组存储需要上传的文件项 */
    let uploadArray = [];

    /* 只筛选出真正需要上传的文件项 */
    for (const item of files) {
        /*-- 一些过滤逻辑 --*/
        uploadArray.push(item);
    }

    /* 创建新的FormData来存储要上传的实际文件 -
    /* 因为Multer基于HTTP工作,所以必须创建新的FormData才能调用另一个端点 */
    let form = new FormData();
    form.append("someData", req.body["someData"]);

    /* 将文件追加到后续用于上传的FormData中 */
    uploadArray.forEach((item) => {
        form.append("files", item.buffer, item.originalname);
    });

    try {
        const postUrl = process.env.MY_URL + "/upload";
        const myHeaders = { headers: { 'Content-Type': `multipart/form-data; boundary=${form._boundary}` } };
        /* 向其他端点发起请求 */
        let uploadResult = await axios.post(postUrl, form, myHeaders);

        if (uploadResult.status == 200) return res.send(uploadResult).status(200);
        /* 问题所在:程序永远到不了这里,但我需要它执行这段逻辑 */
        else {
            return res.send(uploadResult).status(500);
        }
    }
    catch (error) {
        console.error("发生意外错误: ", error);
        return res.send(error)
    }

});

第二个端点(/upload)

app.post("/upload", async (req, res) => {
    try {
        await new Promise((resolve, reject) => {
            /* 将文件上传到云端 */
            uploadOnCloud.array("files")(req, res, (err) => {
                if (err) {
                    console.error("错误已触发!", err)
                    return reject(err);
                }
                else
                    return resolve();
            });
        });
        /* 如果未检测到错误,返回200状态码 */
        res.sendStatus(200);
    }
    catch (error) {
        /* 问题:无法设置状态码或执行其他操作 */
        res.status(500).send(error.message);
    }
});

问题原因

  1. Axios默认不捕获非2xx响应:Axios仅在网络错误、请求超时等场景触发catch,端点返回的5xx错误会被当作正常响应进入uploadResult分支,但你仅判断了status == 200,其他状态码会直接跳过else分支;同时res.send().status()的顺序颠倒,send()会立即结束响应,后续status()无法修改状态码。

  2. 手动设置Content-Type导致解析异常:第一个端点里手动指定FormData的Content-Type头,可能与axios自动生成的格式冲突,导致第二个端点无法正确解析文件,触发错误后响应异常。

  3. 响应发送逻辑存在漏洞:第二个端点未检查响应是否已发送,若Multer回调中存在隐性的响应操作,会导致后续无法正常发送错误响应。

修正建议

第一个端点代码修正

/* 注意:storeInMemory.array("files")会将文件的buffer信息存储在内存中,
/* 这是验证文件所必需的! */
app.post("/filesUpload", storeInMemory.array("files"), async (req, res) => {
    const files = req.files;
    let uploadArray = [];

    /* 只筛选出真正需要上传的文件项 */
    for (const item of files) {
        /*-- 一些过滤逻辑 --*/
        uploadArray.push(item);
    }

    /* 创建新的FormData来存储要上传的实际文件 */
    let form = new FormData();
    form.append("someData", req.body["someData"]);

    /* 将文件追加到后续用于上传的FormData中 */
    uploadArray.forEach((item) => {
        form.append("files", item.buffer, item.originalname);
    });

    try {
        const postUrl = process.env.MY_URL + "/upload";
        // 去掉手动设置的Content-Type,axios会自动处理FormData的边界和类型
        let uploadResult = await axios.post(postUrl, form, {
            // 配置validateStatus,让非2xx状态码触发catch逻辑
            validateStatus: (status) => status >= 200 && status < 300
        });

        // 先设置状态码再发送响应
        return res.status(200).send(uploadResult.data);
    }
    catch (error) {
        console.error("发生意外错误: ", error);
        // 优先提取端点返回的错误信息,无则用默认错误信息
        const errorInfo = error.response?.data || error.message;
        // 使用端点返回的状态码,默认返回500
        return res.status(error.response?.status || 500).send(errorInfo);
    }
});

第二个端点代码修正

app.post("/upload", async (req, res) => {
    try {
        await new Promise((resolve, reject) => {
            /* 将文件上传到云端 */
            uploadOnCloud.array("files")(req, res, (err) => {
                if (err) {
                    console.error("错误已触发!", err)
                    return reject(err);
                }
                resolve();
            });
        });
        /* 如果未检测到错误,返回200状态码 */
        res.sendStatus(200);
    }
    catch (error) {
        // 检查响应是否已发送,避免重复发送
        if (!res.headersSent) {
            res.status(500).send(error.message);
        }
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 02:43:15