multiple属性必须显式写在<input type="file">上才生效;iOS Safari 16.4前不支持;JavaScript需用input.files(FileList)而非value;FormData上传多文件须同key多次append();name建议用复数如files但勿加[]。

multiple 属性必须显式写在 <input type="file"> 上才生效
不加 multiple,哪怕用户按住 Ctrl/Cmd 多选,浏览器也只提交第一个文件——这是最常被忽略的前提。它不是开关配置,而是存在即启用的布尔属性:<input type="file" multiple> ✅;<input type="file" multiple="false"> ❌(依然多选,因为属性存在);<input type="text" multiple> ⚠️(完全无效,被浏览器忽略)。
移动端要特别注意:iOS Safari 在 16.4 之前基本不支持 multiple,部分机型点开仍是单选对话框;Android Chrome 和 Firefox 支持良好。别把“用户说选不了”直接归因于代码,先查系统版本和浏览器型号。
JavaScript 读取多文件必须用 input.files,不是 input.value
input.value 只返回第一个文件的伪造路径(如 C:\fakepath\photo.jpg),且无法反映真实选择数量;真正承载全部文件信息的是 input.files,它是一个只读的 FileList 对象。
常见误操作包括:
立即学习“前端免费学习笔记(深入)”;
- 直接
JSON.stringify(input.files)→ 得到空对象,因为FileList不可序列化 - 用
for...in遍历input.files→ 可能遍历出非文件项(如 length、item 方法) - 没检查
input.files.length就直接访问input.files[0]→ 用户没选文件时会报错
安全做法是:const files = Array.from(input.files) 转成真数组,再用 forEach 或 for 循环处理每个 File 实例,它有 name、size、type、lastModified 等可用字段。
FormData 上传多个文件必须同 key 多次 append()
后端收不到全部文件?大概率卡在这步。错误写法:formData.append('file', file) 循环多次——绝大多数后端框架(Express、Django、PHP)会覆盖前值,最终只剩最后一个。
正确方式是用相同 key 名多次调用 append():
const formData = new FormData();
for (const file of files) {
formData.append('files', file); // 注意 key 是 'files',不是 'file'
}
这样浏览器生成的 multipart 请求中,files 字段会出现多次,后端才能按“同名多值”解析:Express 用 req.files['files'](需配合 upload.array('files') 中间件),PHP 用 $_FILES['files']['name'] 数组遍历。
调试时务必打开 Network 面板,看 Request Payload 里是否列出所有文件条目;如果只看到一个,说明前端没 append 对,或后端中间件没配对。
name 属性建议用复数形式,但别加 [] 后缀
name="files" 比 name="file" 更利于后端识别为数组意图,也符合多数框架默认约定。但不要写成 name="files[]"——这属于 PHP 特定语法糖,在 Express、Fastify、Django 等其他环境里反而可能造成解析歧义或被截断。
另外,accept 属性(如 accept=".pdf,.jpg")只影响文件选择对话框的过滤界面,不影响 multiple 是否启用,也不限制用户实际能选几个文件。真正要做数量限制,得靠 JS 校验 input.files.length 并提示。
iOS 兼容性、FileList 的不可序列化、后端解析逻辑错配——这三个点最容易在线上突然暴露,而且现象相似(“明明选了,后端只收到一个”),排查时得挨个过一遍,不能只盯 HTML 写法。



















