Nest.js+Multer文件上传遇Unexpected end of form错误排查
问题场景
我正在开发一个Nest.js服务端应用,已实现文件上传功能,服务端代码如下:
@Post('upload') @UseInterceptors(FileInterceptor('file')) upload(@UploadedFile() file: Express.Multer.File) { console.log(file); }
使用Postman或Insomnia调用该接口一切正常,但在React项目中使用JavaScript调用时出现问题,React代码如下:
function App() { const [state, setState] = useState(null); const handleChange = e => { console.log(e.target.files); setState(e.target.files[0]); }; const handleSubmit = async e => { e.preventDefault(); const url = 'http://localhost:3000/upload'; const formData = new FormData(); formData.append('file', state); console.log(JSON.stringify(formData)); const response = await fetch(url, { method: 'POST', body: formData, headers: { 'content-type': 'multipart/form-data; boundary=X-INSOMNIA-BOUNDARY', Accept: '*/*' } }); console.log(response); }; return ( <div> <input type="file" name="files" onChange={handleChange} /> <button onClick={handleSubmit}>Upload</button> </div> ); }
此时请求会收到服务器返回的500响应,服务端终端报错:
[Nest] 111577 - 12/10/2022, 19:18:39 ERROR [ExceptionsHandler] Unexpected end of form
Error: Unexpected end of form
错误原因
问题核心在于手动指定了content-type头的边界符。当通过fetch发送FormData时,浏览器会自动生成符合规范的multipart/form-data请求头,其中包含随机生成的边界符(用于分隔表单中的不同字段)。你手动设置的边界符X-INSOMNIA-BOUNDARY和浏览器实际为FormData生成的边界符不匹配,导致服务端的Multer解析器无法正确识别表单数据的结束位置,进而抛出"Unexpected end of form"错误。
另外,JSON.stringify(formData)无法正确序列化FormData对象,这行代码没有实际作用,FormData的序列化由浏览器内部处理。
为什么Postman/Insomnia无此问题
Postman、Insomnia这类API调试工具在发送multipart类型请求时,会自动同步请求头和表单内容的边界符:它们会根据表单数据生成唯一的边界值,同时将该值写入content-type头中,确保服务端解析时能正确匹配,因此不会出现解析错误。
解决方法
删除手动设置的content-type头,让浏览器自动处理请求头的生成:
const response = await fetch(url, { method: 'POST', body: formData, headers: { Accept: '*/*' } });
同时可以移除console.log(JSON.stringify(formData));这行无效代码。
内容的提问来源于stack exchange,提问作者Angel Martinez

