加了 multiple 属性仍只能选一个文件,主因是表单 enctype 缺失、name 未用数组语法、method 非 post;ios safari 16.3 及更早存在静默失效;filelist 需转数组操作;formdata 必须循环 append 单个文件;数量与类型限制须前后端双重校验。

加了 multiple 属性却只能选一个文件?不是浏览器问题,大概率是表单配置或后端接收方式错了。
为什么加了 multiple 还是单文件?三个硬性条件缺一不可
它只是打开多选开关,不保证能传、能收、能用。漏掉任意一条,input.files.length 就永远是 1:
-
enctype="multipart/form-data"必须写在<form></form>标签上——没有它,文件二进制数据根本不会进请求体 -
name属性得带数组语法,比如name="files[]"(PHP)或name="files"(Express/Django),否则后端只拿到最后一个文件 - 表单
method必须是POST;GET会丢文件,且大多数浏览器直接禁用该行为 - iOS Safari 16.3 及更早版本有静默失效 bug:检测方式是
navigator.userAgent.includes('iPhone') && /OS [1-9]\d?_[0-3]/.test(navigator.userAgent),需降级为单次多选 + 手动累加
input.files 不是数组,别直接 .map() 或 .forEach()
它是个只读的 FileList,类数组但没数组方法。强行调用会报 TypeError: input.files.map is not a function:
- 转真数组推荐用
Array.from(input.files),兼容性好、语义清晰 - 也可以用展开语法
[...input.files],但低版本 Safari(≤15.6)不支持 - 别用
input.value拿文件名——它只返回伪造路径(如C:\fakepath\abc.jpg),且多文件时完全不可靠 - 每个
File对象有name、size、type、lastModified,但file.path在所有现代浏览器中都是undefined
用 FormData 上传多个文件,键名写法决定后端能不能全收到
后端收不到全部文件,90% 是因为 append 方式错了。浏览器不会自动拆包,你 append 几次,它就发几个字段:
- ✅ 正确(推荐):
formData.append('files', file)循环调用——后端解析为同名数组(如 Express 的upload.array('files'),PHP 的$_FILES['files']['name']是数组) - ✅ 可选(需后端配合):
formData.append(`file_${i}`, file)——适合要按序处理、单独校验或前端预览绑定的场景 - ❌ 错误:
formData.append('files', input.files)——把整个FileList当一个值塞进去,行为不一致(Chrome 可能凑合,Firefox 直接丢) - ❌ 错误:
formData.append('file', file)只用一次——后端只收到最后一个 - 别手动设
Content-Type请求头,浏览器会自动生成正确的multipart/form-databoundary
限制最多选 5 个文件,HTML 属性做不到,必须前后端双校验
multiple 本身不提供数量限制能力。所谓“限制”,是人为加的约束逻辑:
- 前端 JS 校验:监听
change事件,检查input.files.length > 5,然后截断并提示用户,例如Array.from(input.files).slice(0, 5) - 但用户可能绕过 JS,所以服务端必须二次校验:
$_FILES['files']['name']长度或req.files.length超过 5 就直接拒收 - 别信
accept属性能拦住非法类型——它只是浏览器文件选择器的过滤提示,用户改后缀或拖拽就能绕过 - 文件大小限制也一样:前端用
file.size提前拦截,后端必须用$_FILES['files']['size']或流式读取再校验,否则有被爆破风险
真正容易被忽略的是 iOS Safari 的静默失效和后端对同名多值的解析配置——前者会让测试环境一切正常,线上 iPhone 用户全卡在单文件;后者一旦漏配,不管前端 append 多少次,后端都只看到一个。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











