HTML5的accept属性仅作前端友好提示,无法真正限制文件类型;可靠校验需结合JavaScript读取文件头(magic number)与后端基于文件内容的双重验证。

HTML5 本身不能真正限制文件类型,accept 属性只是提示浏览器过滤文件选择器中的可选文件,用户仍可手动取消过滤、拖入任意文件,或通过开发者工具绕过。真正的类型校验必须在 JavaScript 中读取 File.type 或检查文件头(magic number),并在后端再次验证。
用 accept 属性做前端友好筛选
它只影响文件选择对话框的默认过滤行为,不提供安全保证,但能提升用户体验:
-
accept="image/*":显示所有图片类型(image/jpeg、image/png等) -
accept=".pdf,.docx":按扩展名匹配(注意开头的点不能省略) -
accept="application/pdf":按 MIME 类型匹配(依赖浏览器对文件头的识别能力) - 多个值用逗号分隔,如
accept="image/png,image/jpeg,.svg" - 该属性对
有效,对拖放上传无直接影响
用 JavaScript 检查 File.type 和扩展名
File.type 是浏览器根据文件头或扩展名推测出的 MIME 类型,不可靠(例如空文件、改后缀的文件会返回空字符串或错误类型),需配合扩展名二次判断:
更可靠的校验:读取文件头(Magic Number)
绕过扩展名和 File.type 的欺骗,直接读取文件前几个字节判断真实类型。例如 PNG 文件头是 89 50 4E 47,JPEG 是 FF D8 FF:
立即学习“前端免费学习笔记(深入)”;
- 使用
FileReader.readAsArrayBuffer()读取前 4 字节 - 用
Uint8Array解析字节序列 - 比对预定义的 magic bytes 表(注意字节序和长度)
- 适合关键业务场景(如头像上传、证件扫描件),但增加 JS 开销
示例片段(校验 JPEG):
const reader = new FileReader();
reader.onload = function(e) {
const buffer = e.target.result;
const view = new Uint8Array(buffer, 0, 3);
if (view[0] === 0xFF && view[1] === 0xD8 && view[2] === 0xFF) {
console.log('确认是 JPEG');
}
};
reader.readAsArrayBuffer(file.slice(0, 3));
后端必须重复校验,且不能信任任何前端传来的字段
前端所有校验都可被跳过:accept 可被移除,JS 可被禁用或篡改,Content-Type 请求头可伪造。后端必须:
- 忽略客户端传来的
filename和Content-Type - 用服务端库(如 Python 的
python-magic、Node.js 的file-type)读取文件头识别真实类型 - 对图片类文件,尝试用图像解码库打开(如
PIL、sharp),防止恶意构造的“图片”触发解析漏洞 - 限制文件大小、存储路径、执行权限(如禁止上传到可执行目录)
最常被忽略的一点:即使前端做了完整校验,只要没在服务端重做一遍,就等于没做。文件类型限制不是「前端做一次 + 后端做一次」,而是「后端必须做,前端只是辅助」。











