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

使用Multer上传文件触发Unexpected end of form错误求助

问题描述

尝试从浏览器端向NodeJS服务端上传jpg/jpeg/png格式图片,查阅多份教程及大量论坛帖子后,未找到该特定问题的对应解决方案。

已完成排查操作
  • 已确认multer配置中upload.single('upload')指定的字段名,与前端formData.append('upload', selectedFile, selectedFile.name)使用的键名完全匹配
  • 最初手动配置了请求头,后续查阅相关资料后移除了手动设置的请求头
  • 尝试通过设置了enctype="multipart/form-data"的原生<form action="/upload" method="post">表单提交,仍然出现相同错误
  • 检索到两个相关StackOverflow问题,其中一个仅有一条表述不清晰的回答,另一个暂无回答。其余同类问题的报错多为Unexpected field或Unexpected end of multipart data,对应解决方案和当前问题无关
相关代码

浏览器端原始代码

<body>
  <input type="file" id="file_uploader" name="upload" />
  <button onclick="uploadImage()" class="btn-default">SUBMIT</button>
  
  <!-- OTHER STUFF -->

</body>
<script>
  let selectedFile;
  let uploadData = new FormData();
    
  const fileInput = document.getElementById('file_uploader');
  fileInput.onchange = () => {
    selectedFile = fileInput.files[0];
    uploadData.append('upload', selectedFile, selectedFile.name);
  }

  function uploadImage(){
    fetch('/upload', {
      method: 'POST',
      body: uploadData
    }) 
    .then((response) => {
      console.log(response);
    })
    .catch((error) => {
      console.error('Error: ', error);
    });
  }
</script>

NodeJS端原始代码

let express = require('express');
const multer = require('multer');

//multer options
const upload = multer({
  dest: './upload/',
  limits: {
    fileSize: 1000000,
  }
})

const app = express();

app.post('/upload', upload.single('upload'), (req, res) => {
  res.send();
}, (error, req, res, next) => {
  console.log(error.message);
})

exports.app = functions.https.onRequest(app);
报错日志
Error: Unexpected end of form
     at Multipart._final (C:\Users\p\Downloads\MyInvestmentHub\functions\node_modules\busboy\lib\types\multipart.js:588:17)
     at callFinal (node:internal/streams/writable:694:27)
     at prefinish (node:internal/streams/writable:723:7)
     at finishMaybe (node:internal/streams/writable:733:5)
     at Multipart.Writable.end (node:internal/streams/writable:631:5)
     at onend (node:internal/streams/readable:693:10)
     at processTicksAndRejections (node:internal/process/task_queues:78:11)
修复方案

1. 修正前端FormData初始化逻辑

原代码在全局作用域初始化FormData实例,多次选文件、重复触发上传时会出现字段重复追加、实例状态异常问题,同时缺少空文件校验,可能提交空表单。修正后代码如下:

let selectedFile;
const fileInput = document.getElementById('file_uploader');
fileInput.onchange = () => {
  selectedFile = fileInput.files[0];
}

function uploadImage(){
  // 每次提交时新建FormData实例,避免全局实例污染
  let uploadData = new FormData();
  if (!selectedFile) {
    alert('请先选择要上传的图片');
    return;
  }
  uploadData.append('upload', selectedFile, selectedFile.name);
  fetch('/upload', {
    method: 'POST',
    body: uploadData
  }) 
  .then((response) => {
    console.log(response);
  })
  .catch((error) => {
    console.error('Error: ', error);
  });
}

2. 关闭Serverless环境默认请求体解析(核心修复)

从代码最后一行可判断服务部署在Firebase Functions类Serverless环境,这类环境默认开启请求体解析器,会在请求到达multer之前提前消费完multipart/form-data格式的请求流,导致multer底层依赖的busboy读取不到完整表单数据,抛出Unexpected end of form错误。修改云函数导出配置,关闭默认解析:

// 添加bodyParser: false配置,禁止环境提前解析请求体
exports.app = functions.https.onRequest({bodyParser: false}, app);

3. 修正错误处理中间件写法

原代码将四参数的错误处理中间件直接作为路由回调写在app.post参数内,无法正常捕获multer抛出的解析错误。将错误处理中间件抽离为全局中间件,放在所有路由声明之后:

// 路由放在前面
app.post('/upload', upload.single('upload'), (req, res) => {
  res.send('上传成功');
})

// 全局错误处理中间件放在所有路由之后
app.use((error, req, res, next) => {
  console.log(error.message);
  res.status(400).send({msg: error.message});
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 01:30:27