<input type="file">是唯一合法起点,后续三步(监听change、封装FormData、发送fetch)出错导致90%上传失败;需检查files长度、字段名匹配后端key、禁设Content-Type头、手动校验response.ok。

<input type="file"> 是唯一合法起点,它不上传、不发请求,只负责唤起系统选择框——所有“上传失败”问题,90%出在它之后的三步:监听、封装、发送。
为什么 change 事件里取不到 files
不是浏览器 bug,是没处理空选择或误用了事件时机:
-
event.target.files是FileList,长度为 0 时说明用户点了取消或没选任何文件,需显式判断if (!input.files.length) return - 别在
click或focus里读files,只有change才反映真实选择结果 - 单选场景下用
input.files[0],多选必须遍历或转成数组:Array.from(input.files),直接调.map()会报错
FormData.append() 怎么填字段名才不被后端拒收
字段名(第一个参数)不是随便写的,它等于后端解析时的 key:
- Express + multer 默认按
upload字段收,就得写form.append('upload', file);若后端配了single('avatar'),字段名就必须是'avatar' - 同时传文本参数(如
user_id)也走append():form.append('user_id', '123'),和文件同在一个 multipart 请求体里 - 如果后端收不到文件,先确认日志里有没有 “missing field xxx”,再核对字段名是否大小写/下划线一致
用 fetch 上传时为什么总得 400 错误
最常踩的坑是手贱加 Content-Type 头:
立即学习“前端免费学习笔记(深入)”;
- 绝对不要写
headers: { 'Content-Type': 'multipart/form-data' }—— 这会让浏览器丢掉 boundary,后端解析直接崩溃 - 正确写法:只传
body: form,其他全交给 fetch 自动处理 - 状态码不是 200 时,
fetch不自动 throw,要手动检查:if (!response.ok) throw new Error(response.status) - 常见 400 原因还包括:Nginx 的
client_max_body_size太小、后端框架(如 Flask)误用request.form而非request.files
真正难的不是写对这三步,而是当用户拖着一个 2GB 视频点上传时,你得意识到 change 事件还没触发,浏览器可能已卡死——这时候该上分片、断点、进度条,而不是再调一次 FormData.append()。



















