accept 属性仅影响文件选择器界面显示,不校验文件真实类型;file.type 不可信,需后端通过 magic bytes 校验。

accept 属性只影响文件选择器界面,不改变实际可选文件
加了 accept=".pdf,.xlsx" 后,用户在弹出的系统对话框里默认只看到 PDF 和 Excel 文件,但点“所有文件”就能选任意后缀名。它不拦截、不报错、不校验——只是浏览器 UI 层的友好提示。如果你指望靠这个阻止上传非预期类型,后端一定会收到意外文件。
前端读取 file.type 有风险,不能当真
file.type 是浏览器根据文件扩展名或头部推测出来的 MIME 类型,不可信。比如把 malware.js 改名为 report.pdf,file.type 就会返回 "application/pdf",但实际是 JS 脚本。常见误用包括:
- 直接用
if (file.type !== "image/jpeg")拦截,漏掉.jpg(MIME 是"image/jpeg")和.jpeg(同 MIME),也拦不住伪造的 PDF - 用
file.name.split(".").pop()取后缀,没处理大小写(.JPG→"JPG")、多点文件名(archive.tar.gz)或无后缀文件 - 没判空:用户取消选择时
input.files[0]是undefined,直接访问.type报Cannot read property 'type' of undefined
真正能显示/校验文件类型,得靠后端解析文件头(magic bytes)
前端能做的只是提示和初步过滤,最终判断必须落在服务端。例如:
- Node.js 用
file-type库读前几百字节,识别真实格式,比扩展名可靠得多 - Python 用
python-magic(libmagic 绑定),检查magic number,哪怕文件被重命名也能识别 - Java 用
Apache Tika或jmimemagic做类似事 - 别只校验
Content-Type请求头——它由前端控制,可伪造;必须读原始字节流
显示文件类型给用户看,推荐组合方案
想让用户知道“你选的是什么”,又不想误导,建议这样写:
- change 事件中先取
file.name显示原文件名,再用file.type || "unknown"补充推测类型(加个“(推测)”小字) - 同时读
file.size显示大小,避免用户误传 2GB 视频当简历 PDF - 如果业务强依赖类型(如只收 PNG 图片),前端可做轻量级校验(比如
accept="image/png"+file.type === "image/png"+file.name.toLowerCase().endsWith(".png")),但后端仍要走 magic bytes 流程 - 上传成功后,后端返回真实检测结果(如
{"detected_type": "image/png", "mime_confidence": 0.98}),前端再更新 UI
最易被忽略的一点:前端展示的“类型”永远只是参考,只要后端没读文件头,就等于没校验。accept 和 file.type 都不是开关,而是手电筒——照得见轮廓,照不透内里。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











