前端用 file.size 在 change 事件中即时拦截是体验兜底,accept 属性仅限 mime 或后缀,无法限制图片尺寸;后端需 nginx、框架、语言层三重硬限制且保持一致,并基于魔数而非 type 或 header 校验文件。

前端用 file.size 做即时拦截,不是“限制”而是体验兜底
HTML 的 accept 属性对图片尺寸完全无效,它只管 MIME 类型或后缀提示。真正能拿到文件字节大小的,只有用户选中文件后 JS 读到的 file.size(单位是字节)。这个值在 change 事件里最可靠,别等到 submit 才检查——否则用户可能已选了一个 2GB 视频,点提交才弹错,体验极差。
关键操作包括:
- 监听
input[type="file"]的change事件,取e.target.files[0] - 判断
!file || !file.size防 Safari 或 WebView 返回 0 的情况 - 用
Number(input.getAttribute('data-max-size'))读取配置,避免硬编码 - 校验失败时必须执行
e.target.value = '',否则同名文件不会再次触发change
后端必须设三道硬限制:Nginx、框架、语言层
前端校验可被 curl、Postman、禁用 JS 等方式绕过,后端不设限等于没做。最容易出问题的是三层限制不一致:
- Nginx 的
client_max_body_size必须 ≥ 框架允许的最大值,否则请求根本进不到应用层,直接返回 413,前端收不到任何响应 - Node.js + multer 要配
limits: { fileSize: 10 * 1024 * 1024 },光靠req.file.size判断没用 - PHP 要同步调大
upload_max_filesize和post_max_size,且后者必须 ≥ 前者,否则请求连 PHP 解析器都进不去
漏掉任意一层,上传大文件就可能卡死连接、耗尽内存,甚至触发服务崩溃。
不要依赖 file.type 或请求头的 Content-Type
file.type 在 Windows 下双击选择文件时经常为空,iOS Safari 对 HEIC/WebP 的识别也不稳定。后端更不能信 Content-Type 请求头——改个 header 就能伪造。真实校验必须基于字节流:
- 用
python-magic(Python)或file-type(Node.js)解析文件头魔数,比如 PNG 是89 50 4E 47 - 拒绝
image/bmp、image/vnd.microsoft.icon这类虽属image/*但业务不支持的类型 - 清理文件名:去掉路径(如
../../)、特殊字符,重命名为随机字符串 + 安全后缀
移动端调用摄像头时 accept 会被忽略
加了 capture="environment" 后,iOS 和 Android 都会直接唤起相机,此时 accept 完全失效,拍出来的格式由设备决定(比如 iPhone 默认 HEIC)。你无法靠 HTML 层控制输出格式,真正卡住的地方是服务端对字节流的解析逻辑——前端做的所有事,只是减少无效请求、加快反馈速度,而不是代替后端做决定。
复杂点在于:同一张图在不同设备上可能生成不同后缀、不同 MIME、甚至不同编码(HEIC vs JPEG),但魔数和像素尺寸才是唯一可信依据。别在前端花力气“转格式”,把校验重心放在服务端对原始字节的真实解析上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











