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

Heroku上用Multer处理Post FormData时req.files为空且报错

问题描述

需要实现文件从client --> server1 --> server2的传输流程,注:仅临时使用文件做中转处理,清楚Heroku不适合持久化存储。两台Node服务都部署在Heroku,使用multer处理文件上传:

  • 本地环境下一切正常,但Heroku上出现异常:
    1. server1接收文件正常,但向server2发送时,server1报H12 Request timeout,server2报H28 Client Connection Idle + BadRequestError: request aborted
    2. 直接从client传文件到server2,无超时但req.files为空,multer-s3也存在相同问题

相关代码片段已提供,两台服务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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 09:58:21