change事件里读file.size才是唯一可行的前端检查点,必须在input[type="file"]的change事件中立即获取event.target.files[0].size进行校验,不可延至submit时。

change事件里读file.size才是唯一可行的前端检查点
HTML原生不支持上传前强制限制文件大小,accept属性只管类型,对尺寸完全无效。真正能用的只有 JavaScript 在用户选完文件后立刻读取 event.target.files[0].size(单位是字节)。这个动作必须放在 input[type="file"] 的 change 事件里,不能拖到 submit 时才做——否则用户点了提交才发现选了个 3GB 视频,体验崩坏。
常见错误包括:
- 把校验逻辑写在表单
submit里,导致大文件已触发上传流程才拦截 - 忽略 Safari 或某些 WebView 中
file.size === 0的情况,没加if (!file || !file.size)兜底 - 用
5e6这类科学计数法写阈值,可读性差且易出错,建议统一写成5 * 1024 * 1024
data-max-size 属性让每个 input 独立控制上限
硬编码 2 * 1024 * 1024 在 JS 里会迅速失控:头像要 2MB,商品图要 10MB,PDF 附件要 50MB。改一次 JS 就得全站发版,不现实。
正确做法是让 HTML 自己说话:
- 在 input 上写:
<input type="file" data-max-size="2097152">(即 2MB) - JS 中用
Number(input.getAttribute('data-max-size'))读取,注意必须显式转数字,否则字符串比较会出错('1000000' > 2000000返回true) - 校验失败时,除了提示用户,一定要执行
e.target.value = ''清空 input 值,否则重选同名文件不会再次触发change
后端三道防线缺一不可:client_max_body_size、框架 limits、语言层配置
前端校验只是“友好提示”,curl、Postman、禁用 JS 都能绕过。真正起作用的是后端三层硬限制,漏掉任何一层都可能引发 413 错误或服务卡死。
典型组合(以 Node.js + Nginx 为例):
- Nginx 层:
client_max_body_size 20M—— 必须 ≥ 后端允许的最大值,否则请求连 Nginx 都进不去,前端收不到响应,只会卡住几秒后静默失败 - 框架层(如 multer):
limits: { fileSize: 10 * 1024 * 1024 }—— 只校验req.file.size不够,必须配limits - 语言/运行时层(如 PHP):
upload_max_filesize和post_max_size都得调大,且后者必须 ≥ 前者,否则请求根本到不了 PHP 脚本
最容易被忽略的是 Nginx 和框架限制不一致:比如 Nginx 设了 20M,multer 却只认 5M,用户传 10MB 文件时,后端压根收不到数据,前端既无报错也无响应,排查时容易反复怀疑 JS 写错了。
表单必须设 enctype="multipart/form-data",否则文件字段根本不会发送
哪怕所有校验都写对了,如果 <form></form> 缺少 enctype="multipart/form-data" 或 method 不是 post,浏览器根本不会把文件内容打包进请求体,后端收到的 request.files 一定是空的。
这个配置不是可选项:
-
enctype必须是multipart/form-data,application/x-www-form-urlencoded会丢弃文件数据 - 后端接收字段名必须和
input的name属性严格一致,比如<input name="avatar">,后端就得用request.files.avatar(Flask)或req.files.avatar(Express)去取 - 多文件上传需加
multiple属性,并在 JS 中遍历files列表逐个检查size
实际部署时,最复杂的从来不是怎么写校验逻辑,而是确保 Nginx、框架、语言运行时这三层限制数值对齐,且全部大于等于业务允许的最大单文件尺寸。只要有一层比其他层小,问题就会表现为“上传失败但无明确错误”,排查成本远高于一开始就把三者写清楚。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











