HTML怎么做文件类型检测_HTML文件MIME类型检测方法攻略

作者:袖梨 2026-06-15
HTML 中 files[0].type 不可靠,因其仅基于文件扩展名猜测 MIME 类型;必须通过 FileReader 读取文件头(magic bytes)前端预筛,并由服务端二次校验原始内容。

HTML 本身不提供文件类型检测能力,input[type="file"] 暴露的 files[0].type 只是浏览器根据文件扩展名“猜”的 MIME 类型,不可信,也不能替代真实内容检测。

为什么不能只用 files[0].type

这个字段由浏览器读取文件后缀(如 .jpg)查表得来,不是读文件头。用户改个后缀就能绕过:shell.php 改成 shell.jpgfiles[0].type 就变成 image/jpeg,但实际是 PHP 代码。服务端若只校验这个字段,就会被上传漏洞利用。

  • Chrome / Safari / Firefox 均不解析文件内容,.type 是纯客户端推测值
  • 空后缀、无后缀、双后缀(photo.jpg.php)时该字段常为空字符串或 text/plain
  • 某些系统(如 Windows)会把 CSV 文件报告为 application/vnd.ms-excel,而 Mac 可能报 text/csvtext/plain

前端用 FileReader.readAsArrayBuffer() 读 magic bytes

这是唯一能在不发请求前提下可靠识别格式的方式——直接读文件前若干字节,比对已知 magic number(文件头签名)。

  • 只需读前 12 字节:JPEG(0xFF 0xD8)、PNG(0x89 0x50 0x4E 0x47)、GIF(0x47 0x49 0x46)、WebP(RIFF...WEBP)都够判断
  • 必须用 readAsArrayBuffer(),不能用 readAsText()(会触发编码转换,破坏二进制)
  • file.slice(0, 12) 避免大文件阻塞主线程,也减少内存占用
function detectMimeType(file) {  return new Promise(resolve => {    const reader = new FileReader();    reader.onload = () => {      const buf = new Uint8Array(reader.result);      if (buf[0] === 0xff && buf[1] === 0xd8) resolve('image/jpeg');      else if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) resolve('image/png');      else if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46) resolve('image/gif');      else if (buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 &&               buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50) resolve('image/webp');      else resolve('application/octet-stream');    };    reader.readAsArrayBuffer(file.slice(0, 12));  });}

服务端必须做二次校验,且优先依赖内容探测

Node.js 可用 file-type 库,PHP 用 finfo_file(),Python 用 mimetypes.guess_type() + python-magic。关键点是:必须传入原始文件内容(或临时文件路径),不能只看 $_FILES['f']['type'] 或后缀。

立即学习“前端免费学习笔记(深入)”;

  • Linux/macOS 的 file -i 命令底层就是靠 /usr/share/misc/magic 文件匹配 magic bytes,和前端原理一致
  • PHP 中 $_FILES['f']['type'] 完全来自 HTTP 请求头的 Content-Type,可被 Burp 等工具任意篡改
  • 即使前端做了 magic bytes 校验,服务端仍要重做——因为文件可能在传输中损坏,或前端逻辑被绕过

accept 属性只是 UI 提示,不是安全机制

<input type="file" accept="image/*"> 只影响文件选择器弹窗里默认显示哪些类型,不影响实际可选文件列表(用户可点「所有文件」强行选)。它不会阻止用户选一个 .exe 文件,也不会修改 files[0].type 的值。

  • Chrome 在 macOS 上对 accept=".pdf" 会过滤掉非 PDF 图标,但 Windows 下基本无效
  • 移动端 Safari 忽略大部分 accept 值,仅支持 image/*video/*audio/*
  • 永远不要把 accept 当作校验依据;它只用于改善体验,降低误操作概率

真正可靠的文件类型检测永远需要两层:前端快速预筛(magic bytes)+ 服务端权威判定(内容探测)。任何单侧、仅依赖后缀或 HTTP 头的方案,在生产环境里都等于没设防。

相关文章

精彩推荐