请求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); } });
问题原因
Axios默认不捕获非2xx响应:Axios仅在网络错误、请求超时等场景触发catch,端点返回的5xx错误会被当作正常响应进入
uploadResult分支,但你仅判断了status == 200,其他状态码会直接跳过else分支;同时res.send().status()的顺序颠倒,send()会立即结束响应,后续status()无法修改状态码。手动设置Content-Type导致解析异常:第一个端点里手动指定FormData的
Content-Type头,可能与axios自动生成的格式冲突,导致第二个端点无法正确解析文件,触发错误后响应异常。响应发送逻辑存在漏洞:第二个端点未检查响应是否已发送,若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
相关产品推荐
相关产品推荐

