javascript文件上传核心是三步:选文件→append进formdata→fetch发送;不可手动设content-type,因浏览器需自动注入boundary,否则后端解析失败。

直接用 FormData 封装文件上传,核心就三步:选中文件、append 进去、fetch 发出去——中间不能手动设 Content-Type,也不能把 FormData 转成 JSON 或字符串。
怎么从 <input type="file"> 拿到可用的文件对象
用户点选后,input.files 返回的是 FileList(类数组),不是数组,不能直接用 map 或展开运算符;单文件传 files[0],多文件得遍历。
-
files[0]是File对象,继承自Blob,有name、size、type等属性,可直接传给formData.append() - 如果没选文件,
files.length === 0,必须提前判断,否则files[0]是undefined,append会静默失败 -
multiple属性开启后,files可含多个文件,但append('photos', input.files)不会自动展开——得用for或Array.from(input.files).forEach()
formData.append() 的参数到底怎么填
传文件时,第三个参数 filename 不是可选的“锦上添花”,而是影响服务端接收的关键项:不传时浏览器用 File.name,但若前端想覆盖原始名(比如加时间戳或防重命名冲突),就得显式传。
- 文本字段:
formData.append('username', 'alice')—— 只要两个参数 - 单文件:
formData.append('avatar', input.files[0], 'avatar.jpg')—— 推荐带第三个参数 - 同名多文件:
formData.append('photos', file1); formData.append('photos', file2),后端收到的是数组(如 Express + multer 的req.files.photos) - 别用
formData.append('file', input.files)试图一次塞整个FileList,它会被当做一个整体object,服务端收不到文件内容
为什么 fetch 里绝对不能写 headers: { 'Content-Type': '...' }
浏览器看到 FormData 当作 body,会自动设置 Content-Type: multipart/form-data; boundary=----xxx,这个 boundary 是分隔不同字段的标记。一旦你手动加 Content-Type,浏览器就放弃注入 boundary,后端解析器(如 multer、Django 的 request.FILES)会直接报错或收空。
- 正确写法:
fetch('/upload', { method: 'POST', body: formData })—— 一个 header 都不配 - 错误写法:
headers: { 'Content-Type': 'multipart/form-data' }或'application/json',必炸 - 如果需要带认证 token,只能放
headers: { 'Authorization': 'Bearer xxx' },避开Content-Type
容易被忽略的后端兼容细节
前端看似跑通了,后端收不到文件,八成卡在三个地方:表单字段名不一致、Nginx 限制、或后端没开 multipart 解析。
- 检查后端接收的字段名是否和
append的第一个参数完全一致,包括大小写和下划线(如前端append('user_avatar', f),后端却查avatar) - Nginx 默认限制上传大小为 1MB,需在配置里加
client_max_body_size 20M;并重启 - Node.js 的 Express 必须用
multer中间件;Python Flask 要确认request.files是否非空;Java Spring 要有@RequestParam MultipartFile file注解
最常掉坑的地方不是语法,而是以为 FormData 是个“高级表单序列化工具”——它其实是个二进制载荷构造器,所有字段都按 multipart 协议打包,连空格、换行、边界符都由浏览器严格生成。动它的 Content-Type、转它的类型、漏它的字段名,都会让整个结构失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











