Heroku上用Multer处理Post FormData时req.files为空且报错
问题描述
需要实现文件从client --> server1 --> server2的传输流程,注:仅临时使用文件做中转处理,清楚Heroku不适合持久化存储。两台Node服务都部署在Heroku,使用multer处理文件上传:
- 本地环境下一切正常,但Heroku上出现异常:
- server1接收文件正常,但向server2发送时,server1报
H12 Request timeout,server2报H28 Client Connection Idle+BadRequestError: request aborted - 直接从client传文件到server2,无超时但
req.files为空,multer-s3也存在相同问题
- server1接收文件正常,但向server2发送时,server1报
相关代码片段已提供,两台服务Express配置一致,仅部署在不同dyno。
解决方案
1. 替换Heroku临时磁盘存储为内存存储
Heroku文件系统是临时且无持久化的,multer.diskStorage的本地目录可能触发权限或文件丢失问题,改用内存存储规避磁盘依赖:
// 替换server1和server2的multer存储配置 const multer = require('multer'); const multer_storage = multer.memoryStorage(); // 用内存存储替代磁盘 const form_upload = multer({ storage: multer_storage });
直接从内存读取文件buffer,无需写入磁盘,避免Heroku磁盘的读写限制。
2. 修正server1向server2发送文件的请求配置
使用form-data时,必须显式设置请求长度,否则Heroku反向代理会中断请求:
// server1发送文件的代码修改 const FormData = require('form-data'); const axios = require('axios'); // 从内存存储中获取文件buffer const form = new FormData(); form.append("file1", file1.buffer, { filename: file1.originalname }); form.append("file2", file2.buffer, { filename: file2.originalname }); // 显式设置内容长度,避免请求超时 const headers = { ...form.getHeaders(), 'Content-Length': form.getLengthSync() }; axios.post(URL_SERVER2_UPLOAD_API, form, { headers }) .then(response => { /* 处理响应 */ }) .catch(err => { console.error('传输错误:', err.response?.data || err.message); });
若getLengthSync()报错(大文件场景),改用异步方法:
form.getLength((err, length) => { if (err) throw err; const headers = { ...form.getHeaders(), 'Content-Length': length }; axios.post(URL_SERVER2_UPLOAD_API, form, { headers }); });
3. 调整Express中间件顺序与CORS配置
Express中间件顺序直接影响文件解析,确保multer在CORS之后,且不提前解析multipart表单:
// server.js 修正配置 var express = require('express'); var app = express(); // 先配置CORS app.use(function(req, res, next) { res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Authorization, Content-Type, Accept'); if (req.method === 'OPTIONS') { res.status(200).end(); } else { next(); } }); // 仅解析urlencoded表单,不要在multer前添加express.json() app.use(express.urlencoded({ extended: true })); // 挂载路由(路由内的multer负责处理multipart请求) app.use('/my_route', require("my_route.js")); // 监听Heroku动态分配的端口 var server = app.listen(process.env.PORT || 3000, function() {...});
关键禁止项:不要在multer中间件前使用任何解析multipart/form-data的插件,否则multer无法获取文件。
4. 验证Heroku环境配置
- 确保
URL_SERVER2_UPLOAD_API使用Heroku公网域名(如https://your-server2.herokuapp.com/my_route/upload),禁止使用localhost或内部IP - 用
heroku logs --tail实时查看日志,排查隐藏错误 - 两台服务必须使用
process.env.PORT监听端口,不能硬编码端口号
5. 添加multer错误捕获
在路由内捕获multer的解析错误,排查文件接收失败原因:
// my_route.js 路由修改 routes.post('/upload', function(req, res) { form_upload.any()(req, res, function(err) { if (err) { console.error('Multer解析错误:', err); return res.status(400).json({ error: err.message }); } console.log('接收的文件:', req.files); res.status(200).json({ files: req.files }); }); });
内容的提问来源于stack exchange,提问作者DeLac
相关产品推荐
相关产品推荐

