监听 change 事件并读取 file.size 是唯一可靠起点,Safari、Chrome、Firefox 均同步返回准确字节值;需手动清空 e.target.value 防同名文件不触发事件;前端校验旨在拦截超限上传、节省带宽与等待时间,后端仍须校验;建议用 data-max-size 属性动态配置限制。

监听 change 事件读取 file.size 是唯一可靠起点
浏览器不提供“上传前预知体积”的魔法 API,file.size 是 File 对象上最直接、同步、跨平台可用的属性。Safari(包括 iOS)、Chrome、Firefox 都能立即返回准确值,无需 FileReader 或异步加载——这点常被误认为要“先读再判”,其实完全没必要。
关键动作只有三步:
- 用
addEventListener('change', ...)绑定事件,不能用click或input - 从
e.target.files[0]拿到文件对象(多选时遍历files) - 直接访问
file.size,单位是字节,别错当成 KB
e.target.value = '' 必须手动清空,否则同名文件二次选中失效
这是最常踩的坑:用户选了一个超限文件,提示后没清空 input,接着又选同一个文件——change 事件根本不会触发,因为 DOM 认为值没变。这不是 bug,是规范行为。
正确做法只有一行:
立即学习“前端免费学习笔记(深入)”;
e.target.value = '';
它不是“重置表单”,而是强制让 input 回到未选状态,确保下次选择无论是否同名都能触发事件。别用 form.reset(),它会清掉其他字段;也别跳过这步指望用户“自己点叉”,现实里没人会那样操作。
前端校验不是为了替代后端,而是止损带宽和等待时间
用户传一个 400MB 视频,等 90 秒后收到 413 Request Entity Too Large,这个体验已经崩了。真正耗时的是传输过程,不是后端判断逻辑。
所以前端限制的核心价值在于:
- 在用户点击“上传”前就拦截,避免发起无意义请求
- 绕过 Nginx 默认
client_max_body_size 1m导致的 413(后端压根收不到请求) - 防止弱网下卡在 pending 状态,用户无法分辨是断网还是服务器慢
- 但必须重申:
file.size可被 JS 伪造,后端仍需用Content-Length或流式读取做最终校验
动态限制建议用 data-max-size 属性而非硬编码
一个页面多个上传控件(头像、合同、附件),各自上限不同。硬写死 2 * 1024 * 1024 会让维护变脆弱。
推荐方案是把阈值声明在 HTML 里:
<input type="file" data-max-size="5242880" /> <!-- 5MB -->
JS 中读取即可:
const maxSize = parseInt(e.target.getAttribute('data-max-size') || '0');这样设计更清晰:限制策略由模板控制,逻辑层只负责执行,改上限不用动 JS,也不用维护一堆配置常量。
注意 parseInt 要加默认值,避免 NaN 导致校验失效;移动端 Safari 对该属性读取完全正常,无需降级处理。



















