最直接、跨浏览器稳定的判断方式是用 input.files.length === 0;它比 input.value === '' 更可靠,且需在事件回调中即时读取,校验后须清空 input.value 才能重复触发 change。

用 input.files.length 判断是否选中文件
用户没点“选择文件”或点了“取消”,input.files 是一个空的 FileList,.length 为 0。这是最直接、跨浏览器稳定的判断方式。
常见错误现象:用 input.value === '' 判断——虽然多数情况下也成立,但在某些安卓 WebView 或旧版 Safari 中,value 可能被截断或返回伪造路径,而 files.length 始终反映真实选择状态。
实操建议:
- 必须在事件回调(如
change或按钮点击)中即时读取input.files.length,不要缓存到外部变量 - 如果用 jQuery,记得先转成原生 DOM 元素:
$('#myInput')[0].files.length - 多文件上传时,
length > 0表示至少选了一个,不等于“所有都非空”
服务端用 http.ErrMissingFile 区分“未选”和“选了空文件”
前端空值检查只是体验优化;服务端必须靠 Go 标准库的错误类型语义做精准区分。
http.ErrMissingFile 表示整个 multipart 请求里压根没有该字段名(即用户根本没触发文件选择),而 r.FormFile("xxx") 返回 nil 错误只说明字段存在——哪怕它是个 0 字节文件。
实操建议:
- 先做
switch err分支判断,case http.ErrMissingFile就是用户未选文件 - 若
err == nil,再进一步用io.ReadFull(file, make([]byte, 1))或file.Read(make([]byte, 1))检查是否真为空内容——不能只信header.Size == 0,该字段可被篡改 - 校验失败后,务必调用
file.Close(),否则可能泄漏文件句柄
避免 required 属性的幻觉
required 对 type="file" 的作用非常有限:它只在表单提交时拦截 files.length === 0 的情况,但无法阻止用户选中一个 0 字节文件后提交。
更关键的是,Chrome/Firefox 的原生提示文案不可定制,且移动端部分浏览器(如 iOS Safari)对 required 的支持不一致,容易漏掉校验。
实操建议:
- 不要依赖
required做唯一校验,它只适合辅助提示,不是安全边界 - 若需统一提示风格,应屏蔽原生气泡:
input.addEventListener('invalid', e => e.preventDefault()),再用 JS 手动显示自定义提示 -
required和multiple共存时,只要files.length >= 1就算通过,不校验每个文件是否有效
清空 input.value 才能重复触发 change
用户选了一个大文件,校验失败后没清空 input.value,再选同一个文件时 change 事件根本不会触发——因为 DOM 值没变。
这是前端文件校验中最常被忽略的细节,导致“第一次失败后永远无法重试”。
实操建议:
- 在校验逻辑末尾,无论成功失败,只要涉及用户交互反馈,都执行
input.value = '' - 如果用了 Vue/React,注意
v-model或受控组件可能掩盖该行为,仍需手动操作原生input元素 - 清空后可选地调用
input.dispatchEvent(new Event('change'))触发后续清理逻辑(如移除预览图)
files.length === 0、header.Size == 0、required 这三者覆盖的其实是三个不同层次的问题——漏掉任意一层,就可能让空文件或恶意构造的假文件穿过防线。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











